mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)
* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 * fix pipelines * fix strict-null-checks * add missing files
This commit is contained in:
@@ -12,12 +12,12 @@ import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IBreakpointUpdateData, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
@@ -30,6 +30,8 @@ import { memoize } from 'vs/base/common/decorators';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -45,7 +47,7 @@ const breakpointHelperDecoration: IModelDecorationOptions = {
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray<IBreakpoint>, debugService: IDebugService): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray<IBreakpoint>, debugService: IDebugService, debugSettings: IDebugConfiguration): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
const result: { range: Range; options: IModelDecorationOptions; }[] = [];
|
||||
breakpoints.forEach((breakpoint) => {
|
||||
if (breakpoint.lineNumber <= model.getLineCount()) {
|
||||
@@ -56,7 +58,7 @@ function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArr
|
||||
);
|
||||
|
||||
result.push({
|
||||
options: getBreakpointDecorationOptions(model, breakpoint, debugService),
|
||||
options: getBreakpointDecorationOptions(model, breakpoint, debugService, debugSettings),
|
||||
range
|
||||
});
|
||||
}
|
||||
@@ -65,7 +67,7 @@ function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArr
|
||||
return result;
|
||||
}
|
||||
|
||||
function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, debugService: IDebugService): IModelDecorationOptions {
|
||||
function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, debugService: IDebugService, debugSettings: IDebugConfiguration): IModelDecorationOptions {
|
||||
const { className, message } = getBreakpointMessageAndClassName(debugService, breakpoint);
|
||||
let glyphMarginHoverMessage: MarkdownString | undefined;
|
||||
|
||||
@@ -78,11 +80,22 @@ function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoi
|
||||
}
|
||||
}
|
||||
|
||||
let overviewRulerDecoration: IModelDecorationOverviewRulerOptions | null;
|
||||
if (debugSettings.showBreakpointsInOverviewRuler) {
|
||||
overviewRulerDecoration = {
|
||||
color: 'rgb(124, 40, 49)',
|
||||
position: OverviewRulerLane.Left
|
||||
};
|
||||
} else {
|
||||
overviewRulerDecoration = null;
|
||||
}
|
||||
|
||||
return {
|
||||
glyphMarginClassName: className,
|
||||
glyphMarginHoverMessage,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-placeholder` : undefined
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-placeholder` : undefined,
|
||||
overviewRuler: overviewRulerDecoration
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,16 +151,13 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService);
|
||||
this.registerListeners();
|
||||
this.setDecorationsScheduler = new RunOnceScheduler(() => this.setDecorations(), 30);
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return BREAKPOINT_EDITOR_CONTRIBUTION_ID;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => {
|
||||
const data = e.target.detail as IMarginData;
|
||||
@@ -220,7 +230,7 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => {
|
||||
let showBreakpointHintAtLineNumber = -1;
|
||||
const model = this.editor.getModel();
|
||||
if (model && e.target.position && e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) &&
|
||||
if (model && e.target.position && (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) &&
|
||||
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (!data.isAfterLines) {
|
||||
@@ -243,6 +253,11 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidChangeModelDecorations(() => this.onModelDecorationsChanged()));
|
||||
this.toDispose.push(this.configurationService.onDidChangeConfiguration(async (e) => {
|
||||
if (e.affectsConfiguration('debug.showBreakpointsInOverviewRuler')) {
|
||||
await this.setDecorations();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private getContextMenuActions(breakpoints: ReadonlyArray<IBreakpoint>, uri: URI, lineNumber: number, column?: number): Array<IAction | ContextSubMenu> {
|
||||
@@ -305,7 +320,7 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(lineNumber, column))
|
||||
() => Promise.resolve(this.showBreakpointWidget(lineNumber, column, BreakpointWidgetContext.CONDITION))
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addLogPoint',
|
||||
@@ -357,7 +372,8 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
const activeCodeEditor = this.editor;
|
||||
const model = activeCodeEditor.getModel();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri });
|
||||
const desiredBreakpointDecorations = createBreakpointDecorations(model, breakpoints, this.debugService);
|
||||
const debugSettings = this.configurationService.getValue<IDebugConfiguration>('debug');
|
||||
const desiredBreakpointDecorations = createBreakpointDecorations(model, breakpoints, this.debugService, debugSettings);
|
||||
|
||||
try {
|
||||
this.ignoreDecorationsChangedEvent = true;
|
||||
@@ -542,6 +558,20 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable {
|
||||
onHide: () => dispose(actions)
|
||||
});
|
||||
}));
|
||||
|
||||
const updateSize = () => {
|
||||
const lineHeight = this.editor.getOption(EditorOption.lineHeight);
|
||||
this.domNode.style.height = `${lineHeight}px`;
|
||||
this.domNode.style.width = `${Math.ceil(0.8 * lineHeight)}px`;
|
||||
this.domNode.style.marginLeft = `${Math.ceil(0.35 * lineHeight)}px`;
|
||||
};
|
||||
updateSize();
|
||||
|
||||
this.toDispose.push(this.editor.onDidChangeConfiguration(c => {
|
||||
if (c.hasChanged(EditorOption.fontSize) || c.hasChanged(EditorOption.lineHeight)) {
|
||||
updateSize();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@memoize
|
||||
@@ -572,4 +602,4 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(BreakpointEditorContribution);
|
||||
registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution);
|
||||
|
||||
@@ -54,8 +54,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
private hitCountInput = '';
|
||||
private logMessageInput = '';
|
||||
private breakpoint: IBreakpoint | undefined;
|
||||
private context: Context;
|
||||
|
||||
constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, private context: Context,
|
||||
constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, context: Context | undefined,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@@ -74,7 +75,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
this.breakpoint = breakpoints.length ? breakpoints[0] : undefined;
|
||||
}
|
||||
|
||||
if (this.context === undefined) {
|
||||
if (context === undefined) {
|
||||
if (this.breakpoint && !this.breakpoint.condition && !this.breakpoint.hitCondition && this.breakpoint.logMessage) {
|
||||
this.context = Context.LOG_MESSAGE;
|
||||
} else if (this.breakpoint && !this.breakpoint.condition && this.breakpoint.hitCondition) {
|
||||
@@ -82,6 +83,8 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
} else {
|
||||
this.context = Context.CONDITION;
|
||||
}
|
||||
} else {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(e => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
|
||||
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 { Constants } from 'vs/base/common/uint';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list';
|
||||
@@ -161,21 +161,19 @@ export class BreakpointsView extends ViewletPanel {
|
||||
|
||||
const breakpointType = element instanceof Breakpoint && element.logMessage ? nls.localize('Logpoint', "Logpoint") : nls.localize('Breakpoint', "Breakpoint");
|
||||
if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) {
|
||||
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, () => {
|
||||
actions.push(new Action('workbench.action.debug.openEditorAndEditBreakpoint', nls.localize('editBreakpoint', "Edit {0}...", breakpointType), '', true, async () => {
|
||||
if (element instanceof Breakpoint) {
|
||||
return openBreakpointSource(element, false, false, this.debugService, this.editorService).then(editor => {
|
||||
if (editor) {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
const editor = await openBreakpointSource(element, false, false, this.debugService, this.editorService);
|
||||
if (editor) {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
|
||||
this.onBreakpointsChange();
|
||||
}
|
||||
|
||||
this.debugService.getViewModel().setSelectedFunctionBreakpoint(element);
|
||||
this.onBreakpointsChange();
|
||||
return Promise.resolve(undefined);
|
||||
}));
|
||||
actions.push(new Separator());
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { STOP_ID, STOP_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, RESTART_SESSION_ID, RESTART_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STEP_INTO_LABEL, STEP_INTO_ID, STEP_OUT_LABEL, STEP_OUT_ID, PAUSE_ID, PAUSE_LABEL, CONTINUE_ID, CONTINUE_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -82,8 +83,16 @@ export class CallStackView extends ViewletPanel {
|
||||
this.pauseMessageLabel.title = thread.stoppedDetails.text || '';
|
||||
dom.toggleClass(this.pauseMessageLabel, 'exception', thread.stoppedDetails.reason === 'exception');
|
||||
this.pauseMessage.hidden = false;
|
||||
if (this.toolbar) {
|
||||
this.toolbar.setActions([])();
|
||||
}
|
||||
|
||||
} else {
|
||||
this.pauseMessage.hidden = true;
|
||||
if (this.toolbar) {
|
||||
const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer');
|
||||
this.toolbar.setActions([collapseAction])();
|
||||
}
|
||||
}
|
||||
|
||||
this.needsRefresh = false;
|
||||
|
||||
@@ -262,8 +262,14 @@ configurationRegistry.registerConfiguration({
|
||||
},
|
||||
'debug.onTaskErrors': {
|
||||
enum: ['debugAnyway', 'showErrors', 'prompt'],
|
||||
enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user.")],
|
||||
description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."),
|
||||
default: 'prompt'
|
||||
},
|
||||
'debug.showBreakpointsInOverviewRuler': {
|
||||
type: 'boolean',
|
||||
description: nls.localize({ comment: ['This is the description for a setting'], key: 'showBreakpointsInOverviewRuler' }, "Controls whether breakpoints should be shown in the overview ruler."),
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
private selected = 0;
|
||||
|
||||
constructor(
|
||||
private context: any,
|
||||
private context: unknown,
|
||||
private action: IAction,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@@ -122,8 +122,8 @@ export class StartDebugActionViewItem implements IActionViewItem {
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(attachStylerCallback(this.themeService, { selectBorder }, colors => {
|
||||
this.container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : null;
|
||||
selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : null;
|
||||
this.container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : '';
|
||||
selectBoxContainer.style.borderLeft = colors.selectBorder ? `1px solid ${colors.selectBorder}` : '';
|
||||
}));
|
||||
|
||||
this.updateOptions();
|
||||
|
||||
@@ -12,23 +12,17 @@ import { Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/contrib/d
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { startDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
|
||||
export abstract class AbstractDebugAction extends Action {
|
||||
|
||||
protected toDispose: IDisposable[];
|
||||
|
||||
constructor(
|
||||
id: string, label: string, cssClass: string,
|
||||
@IDebugService protected debugService: IDebugService,
|
||||
@IKeybindingService protected keybindingService: IKeybindingService,
|
||||
) {
|
||||
super(id, label, cssClass, false);
|
||||
this.toDispose = [];
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => this.updateEnablement(state)));
|
||||
this._register(this.debugService.onDidChangeState(state => this.updateEnablement(state)));
|
||||
|
||||
this.updateLabel(label);
|
||||
this.updateEnablement();
|
||||
@@ -56,16 +50,11 @@ export abstract class AbstractDebugAction extends Action {
|
||||
protected isEnabled(_: State): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigureAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.action.debug.configure';
|
||||
static LABEL = nls.localize('openLaunchJson', "Open {0}", 'launch.json');
|
||||
static readonly LABEL = nls.localize('openLaunchJson', "Open {0}", 'launch.json');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IDebugService debugService: IDebugService,
|
||||
@@ -74,7 +63,7 @@ export class ConfigureAction extends AbstractDebugAction {
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(id, label, 'debug-action configure', debugService, keybindingService);
|
||||
this.toDispose.push(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass()));
|
||||
this._register(debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateClass()));
|
||||
this.updateClass();
|
||||
}
|
||||
|
||||
@@ -92,19 +81,17 @@ export class ConfigureAction extends AbstractDebugAction {
|
||||
this.class = configurationCount > 0 ? 'debug-action configure' : 'debug-action configure notification';
|
||||
}
|
||||
|
||||
run(event?: any): Promise<any> {
|
||||
async run(event?: any): Promise<any> {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.notificationService.info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const sideBySide = !!(event && (event.ctrlKey || event.metaKey));
|
||||
const configurationManager = this.debugService.getConfigurationManager();
|
||||
if (!configurationManager.selectedConfiguration.launch) {
|
||||
configurationManager.selectConfiguration(configurationManager.getLaunches()[0]);
|
||||
if (configurationManager.selectedConfiguration.launch) {
|
||||
return configurationManager.selectedConfiguration.launch.openConfigFile(sideBySide, false);
|
||||
}
|
||||
|
||||
return configurationManager.selectedConfiguration.launch!.openConfigFile(sideBySide, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,18 +103,18 @@ export class StartAction extends AbstractDebugAction {
|
||||
@IDebugService debugService: IDebugService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IHistoryService private readonly historyService: IHistoryService
|
||||
) {
|
||||
super(id, label, 'debug-action start', debugService, keybindingService);
|
||||
|
||||
this.toDispose.push(this.debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateEnablement()));
|
||||
this.toDispose.push(this.debugService.onDidNewSession(() => this.updateEnablement()));
|
||||
this.toDispose.push(this.debugService.onDidEndSession(() => this.updateEnablement()));
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkbenchState(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getConfigurationManager().onDidSelectConfiguration(() => this.updateEnablement()));
|
||||
this._register(this.debugService.onDidNewSession(() => this.updateEnablement()));
|
||||
this._register(this.debugService.onDidEndSession(() => this.updateEnablement()));
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<boolean> {
|
||||
return startDebugging(this.debugService, this.historyService, this.isNoDebug());
|
||||
const { launch, name } = this.debugService.getConfigurationManager().selectedConfiguration;
|
||||
return this.debugService.startDebugging(launch, name, { noDebug: this.isNoDebug() });
|
||||
}
|
||||
|
||||
protected isNoDebug(): boolean {
|
||||
@@ -165,7 +152,7 @@ export class RunAction extends StartAction {
|
||||
|
||||
export class SelectAndStartAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.action.debug.selectandstart';
|
||||
static LABEL = nls.localize('selectAndStartDebugging', "Select and Start Debugging");
|
||||
static readonly LABEL = nls.localize('selectAndStartDebugging', "Select and Start Debugging");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IDebugService debugService: IDebugService,
|
||||
@@ -182,7 +169,7 @@ export class SelectAndStartAction extends AbstractDebugAction {
|
||||
|
||||
export class RemoveBreakpointAction extends Action {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeBreakpoint';
|
||||
static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint");
|
||||
static readonly LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService private readonly debugService: IDebugService) {
|
||||
super(id, label, 'debug-action remove');
|
||||
@@ -196,11 +183,11 @@ export class RemoveBreakpointAction extends Action {
|
||||
|
||||
export class RemoveAllBreakpointsAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeAllBreakpoints';
|
||||
static LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints");
|
||||
static readonly LABEL = nls.localize('removeAllBreakpoints', "Remove All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action remove-all', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -215,11 +202,11 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction {
|
||||
|
||||
export class EnableAllBreakpointsAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.enableAllBreakpoints';
|
||||
static LABEL = nls.localize('enableAllBreakpoints', "Enable All Breakpoints");
|
||||
static readonly LABEL = nls.localize('enableAllBreakpoints', "Enable All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action enable-all-breakpoints', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -234,11 +221,11 @@ export class EnableAllBreakpointsAction extends AbstractDebugAction {
|
||||
|
||||
export class DisableAllBreakpointsAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.disableAllBreakpoints';
|
||||
static LABEL = nls.localize('disableAllBreakpoints', "Disable All Breakpoints");
|
||||
static readonly LABEL = nls.localize('disableAllBreakpoints', "Disable All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action disable-all-breakpoints', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -253,14 +240,14 @@ export class DisableAllBreakpointsAction extends AbstractDebugAction {
|
||||
|
||||
export class ToggleBreakpointsActivatedAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction';
|
||||
static ACTIVATE_LABEL = nls.localize('activateBreakpoints', "Activate Breakpoints");
|
||||
static DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints");
|
||||
static readonly ACTIVATE_LABEL = nls.localize('activateBreakpoints', "Activate Breakpoints");
|
||||
static readonly DEACTIVATE_LABEL = nls.localize('deactivateBreakpoints', "Deactivate Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action breakpoints-activate', debugService, keybindingService);
|
||||
this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL);
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => {
|
||||
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => {
|
||||
this.updateLabel(this.debugService.getModel().areBreakpointsActivated() ? ToggleBreakpointsActivatedAction.DEACTIVATE_LABEL : ToggleBreakpointsActivatedAction.ACTIVATE_LABEL);
|
||||
this.updateEnablement();
|
||||
}));
|
||||
@@ -277,11 +264,11 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction {
|
||||
|
||||
export class ReapplyBreakpointsAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.reapplyBreakpointsAction';
|
||||
static LABEL = nls.localize('reapplyAllBreakpoints', "Reapply All Breakpoints");
|
||||
static readonly LABEL = nls.localize('reapplyAllBreakpoints', "Reapply All Breakpoints");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, '', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -297,16 +284,15 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction {
|
||||
|
||||
export class AddFunctionBreakpointAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.addFunctionBreakpointAction';
|
||||
static LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint");
|
||||
static readonly LABEL = nls.localize('addFunctionBreakpoint', "Add Function Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action add-function-breakpoint', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
this.debugService.addFunctionBreakpoint();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
protected isEnabled(_: State): boolean {
|
||||
@@ -317,17 +303,16 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction {
|
||||
|
||||
export class AddWatchExpressionAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.addWatchExpression';
|
||||
static LABEL = nls.localize('addWatchExpression', "Add Expression");
|
||||
static readonly LABEL = nls.localize('addWatchExpression', "Add Expression");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action add-watch-expression', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement()));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getViewModel().onDidSelectExpression(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
this.debugService.addWatchExpression();
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
protected isEnabled(_: State): boolean {
|
||||
@@ -338,16 +323,15 @@ export class AddWatchExpressionAction extends AbstractDebugAction {
|
||||
|
||||
export class RemoveAllWatchExpressionsAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeAllWatchExpressions';
|
||||
static LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions");
|
||||
static readonly LABEL = nls.localize('removeAllWatchExpressions', "Remove All Expressions");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action remove-all', debugService, keybindingService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement()));
|
||||
this._register(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement()));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
this.debugService.removeWatchExpressions();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
protected isEnabled(_: State): boolean {
|
||||
@@ -357,7 +341,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction {
|
||||
|
||||
export class FocusSessionAction extends AbstractDebugAction {
|
||||
static readonly ID = 'workbench.action.debug.focusProcess';
|
||||
static LABEL = nls.localize('focusSession', "Focus Session");
|
||||
static readonly LABEL = nls.localize('focusSession', "Focus Session");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IDebugService debugService: IDebugService,
|
||||
@@ -367,23 +351,21 @@ export class FocusSessionAction extends AbstractDebugAction {
|
||||
super(id, label, '', debugService, keybindingService);
|
||||
}
|
||||
|
||||
run(session: IDebugSession): Promise<any> {
|
||||
this.debugService.focusStackFrame(undefined, undefined, session, true);
|
||||
async run(session: IDebugSession): Promise<any> {
|
||||
await this.debugService.focusStackFrame(undefined, undefined, session, true);
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (stackFrame) {
|
||||
return stackFrame.openInEditor(this.editorService, true);
|
||||
await stackFrame.openInEditor(this.editorService, true);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyValueAction extends Action {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.copyValue';
|
||||
static LABEL = nls.localize('copyValue', "Copy Value");
|
||||
static readonly LABEL = nls.localize('copyValue', "Copy Value");
|
||||
|
||||
constructor(
|
||||
id: string, label: string, private value: any, private context: string,
|
||||
id: string, label: string, private value: Variable | string, private context: string,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IClipboardService private readonly clipboardService: IClipboardService
|
||||
) {
|
||||
@@ -395,15 +377,19 @@ export class CopyValueAction extends Action {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
const session = this.debugService.getViewModel().focusedSession;
|
||||
|
||||
if (this.value instanceof Variable && stackFrame && session && this.value.evaluateName) {
|
||||
if (typeof this.value === 'string') {
|
||||
return this.clipboardService.writeText(this.value);
|
||||
}
|
||||
|
||||
if (stackFrame && session && this.value.evaluateName) {
|
||||
try {
|
||||
const evaluation = await session.evaluate(this.value.evaluateName, stackFrame.frameId, this.context);
|
||||
this.clipboardService.writeText(evaluation.body.result);
|
||||
} catch (e) {
|
||||
this.clipboardService.writeText(this.value.value);
|
||||
}
|
||||
} else {
|
||||
this.clipboardService.writeText(this.value.value);
|
||||
}
|
||||
|
||||
return this.clipboardService.writeText(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
|
||||
@@ -27,8 +27,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { startDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
@@ -60,10 +58,10 @@ export const DISCONNECT_LABEL = nls.localize('disconnect', "Disconnect");
|
||||
export const STOP_LABEL = nls.localize('stop', "Stop");
|
||||
export const CONTINUE_LABEL = nls.localize('continueDebug', "Continue");
|
||||
|
||||
function getThreadAndRun(accessor: ServicesAccessor, threadId: number | undefined, run: (thread: IThread) => Promise<void>): void {
|
||||
async function getThreadAndRun(accessor: ServicesAccessor, threadId: number | any, run: (thread: IThread) => Promise<void>): Promise<void> {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
let thread: IThread | undefined;
|
||||
if (threadId) {
|
||||
if (typeof threadId === 'number') {
|
||||
debugService.getModel().getSessions().forEach(s => {
|
||||
if (!thread) {
|
||||
thread = s.getThread(threadId);
|
||||
@@ -79,7 +77,7 @@ function getThreadAndRun(accessor: ServicesAccessor, threadId: number | undefine
|
||||
}
|
||||
|
||||
if (thread) {
|
||||
run(thread).then(undefined, onUnexpectedError);
|
||||
await run(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +102,10 @@ function getFrame(debugService: IDebugService, frameId: string | undefined): ISt
|
||||
|
||||
export function registerCommands(): void {
|
||||
|
||||
// These commands are used in call stack context menu, call stack inline actions, command pallete, debug toolbar, mac native touch bar
|
||||
// When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id
|
||||
// Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command pallete) we do not pass any id and just take whatever is the focussed thread
|
||||
// Same for stackFrame commands and session commands.
|
||||
CommandsRegistry.registerCommand({
|
||||
id: COPY_STACK_TRACE_ID,
|
||||
handler: async (accessor: ServicesAccessor, _: string, frameId: string | undefined) => {
|
||||
@@ -119,21 +121,21 @@ export function registerCommands(): void {
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: REVERSE_CONTINUE_ID,
|
||||
handler: (accessor: ServicesAccessor, threadId: number | undefined) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, thread => thread.reverseContinue());
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: STEP_BACK_ID,
|
||||
handler: (accessor: ServicesAccessor, threadId: number | undefined) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, thread => thread.stepBack());
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: TERMINATE_THREAD_ID,
|
||||
handler: (accessor: ServicesAccessor, threadId: number | undefined) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, thread => thread.terminate());
|
||||
}
|
||||
});
|
||||
@@ -199,8 +201,8 @@ export function registerCommands(): void {
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
const historyService = accessor.get(IHistoryService);
|
||||
startDebugging(debugService, historyService, false);
|
||||
const { launch, name } = debugService.getConfigurationManager().selectedConfiguration;
|
||||
debugService.startDebugging(launch, name, { noDebug: false });
|
||||
} else {
|
||||
session.removeReplExpressions();
|
||||
debugService.restartSession(session).then(undefined, onUnexpectedError);
|
||||
@@ -213,7 +215,7 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F10,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
|
||||
handler: (accessor: ServicesAccessor, threadId: number) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, (thread: IThread) => thread.next());
|
||||
}
|
||||
});
|
||||
@@ -223,7 +225,7 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging
|
||||
primary: KeyCode.F11,
|
||||
when: CONTEXT_IN_DEBUG_MODE,
|
||||
handler: (accessor: ServicesAccessor, threadId: number) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, (thread: IThread) => thread.stepIn());
|
||||
}
|
||||
});
|
||||
@@ -233,7 +235,7 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyMod.Shift | KeyCode.F11,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
|
||||
handler: (accessor: ServicesAccessor, threadId: number) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, (thread: IThread) => thread.stepOut());
|
||||
}
|
||||
});
|
||||
@@ -243,7 +245,7 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F6,
|
||||
when: CONTEXT_DEBUG_STATE.isEqualTo('running'),
|
||||
handler: (accessor: ServicesAccessor, threadId: number) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, thread => thread.pause());
|
||||
}
|
||||
});
|
||||
@@ -292,7 +294,7 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
primary: KeyCode.F5,
|
||||
when: CONTEXT_IN_DEBUG_MODE,
|
||||
handler: (accessor: ServicesAccessor, threadId: number | undefined) => {
|
||||
handler: (accessor: ServicesAccessor, threadId: number | any) => {
|
||||
getThreadAndRun(accessor, threadId, thread => thread.continue());
|
||||
}
|
||||
});
|
||||
@@ -445,14 +447,11 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: (accessor) => {
|
||||
handler: async (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();
|
||||
});
|
||||
const viewlet = await viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) as IExtensionsViewlet;
|
||||
viewlet.search('tag:debuggers @sort:installs');
|
||||
viewlet.focus();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -461,24 +460,23 @@ export function registerCommands(): void {
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: undefined,
|
||||
handler: (accessor, launchUri: string) => {
|
||||
handler: async (accessor, launchUri: string) => {
|
||||
const manager = accessor.get(IDebugService).getConfigurationManager();
|
||||
if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
accessor.get(INotificationService).info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
const launch = manager.getLaunches().filter(l => l.uri.toString() === launchUri).pop() || manager.selectedConfiguration.launch;
|
||||
|
||||
return launch!.openConfigFile(false, false).then(({ editor, created }) => {
|
||||
const launch = manager.getLaunches().filter(l => l.uri.toString() === launchUri).pop() || manager.selectedConfiguration.launch;
|
||||
if (launch) {
|
||||
const { editor, created } = await launch.openConfigFile(false, false);
|
||||
if (editor && !created) {
|
||||
const codeEditor = <ICodeEditor>editor.getControl();
|
||||
if (codeEditor) {
|
||||
return codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration();
|
||||
await codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import * as resources from 'vs/base/common/resources';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditor } from 'vs/workbench/common/editor';
|
||||
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -21,7 +20,7 @@ import { IFileService } from 'vs/platform/files/common/files';
|
||||
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, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
|
||||
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -32,10 +31,12 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat
|
||||
import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/contrib/debug/common/debugSchemas';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { sequence } from 'vs/base/common/async';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
|
||||
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
jsonRegistry.registerSchema(launchSchemaId, launchSchema);
|
||||
@@ -57,7 +58,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
private debugConfigurationTypeContext: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
private debugService: IDebugService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@@ -65,21 +65,29 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IHistoryService historyService: IHistoryService
|
||||
) {
|
||||
this.configProviders = [];
|
||||
this.adapterDescriptorFactories = [];
|
||||
this.debuggers = [];
|
||||
this.toDispose = [];
|
||||
this.initLaunches();
|
||||
this.registerListeners(lifecycleService);
|
||||
this.registerListeners();
|
||||
const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE);
|
||||
const previousSelectedLaunch = this.launches.filter(l => l.uri.toString() === previousSelectedRoot).pop();
|
||||
this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService);
|
||||
if (previousSelectedLaunch) {
|
||||
this.selectConfiguration(previousSelectedLaunch, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE));
|
||||
} else if (this.launches.length > 0) {
|
||||
const rootUri = historyService.getLastActiveWorkspaceRoot();
|
||||
let launch = this.getLaunch(rootUri);
|
||||
if (!launch || launch.getConfigurationNames().length === 0) {
|
||||
launch = first(this.launches, l => !!(l && l.getConfigurationNames().length), launch) || this.launches[0];
|
||||
}
|
||||
|
||||
this.selectConfiguration(launch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,31 +190,27 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
return providers.length > 0;
|
||||
}
|
||||
|
||||
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined> {
|
||||
return this.activateDebuggers('onDebugResolve', type).then(() => {
|
||||
// pipe the config through the promises sequentially. Append at the end the '*' types
|
||||
const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration)
|
||||
.concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration));
|
||||
async resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, config: IConfig, token: CancellationToken): Promise<IConfig | null | undefined> {
|
||||
await this.activateDebuggers('onDebugResolve', type);
|
||||
// pipe the config through the promises sequentially. Append at the end the '*' types
|
||||
const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration)
|
||||
.concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration));
|
||||
|
||||
return providers.reduce((promise, provider) => {
|
||||
return promise.then(config => {
|
||||
if (config) {
|
||||
return provider.resolveDebugConfiguration!(folderUri, config, token);
|
||||
} else {
|
||||
return Promise.resolve(config);
|
||||
}
|
||||
});
|
||||
}, Promise.resolve(debugConfiguration));
|
||||
});
|
||||
await sequence(providers.map(provider => async () => {
|
||||
config = (await provider.resolveDebugConfiguration!(folderUri, config, token)) || config;
|
||||
}));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise<any[]> {
|
||||
return this.activateDebuggers('onDebugInitialConfigurations')
|
||||
.then(() => Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token)))
|
||||
.then(results => results.reduce((first, second) => first.concat(second), [])));
|
||||
async provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise<any[]> {
|
||||
await this.activateDebuggers('onDebugInitialConfigurations');
|
||||
const results = await Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token)));
|
||||
|
||||
return results.reduce((first, second) => first.concat(second), []);
|
||||
}
|
||||
|
||||
private registerListeners(lifecycleService: ILifecycleService): void {
|
||||
private registerListeners(): void {
|
||||
debuggersExtPoint.setHandler((extensions, delta) => {
|
||||
delta.added.forEach(added => {
|
||||
added.value.forEach(rawAdapter => {
|
||||
@@ -242,12 +246,6 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
delta.removed.forEach(removed => {
|
||||
const removedTypes = removed.value.map(rawAdapter => rawAdapter.type);
|
||||
this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1);
|
||||
this.debugService.getModel().getSessions().forEach(s => {
|
||||
// Stop sessions if their debugger has been removed
|
||||
if (removedTypes.indexOf(s.configuration.type) >= 0) {
|
||||
this.debugService.stopSession(s).then(undefined, onUnexpectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// update the schema to include all attributes, snippets and types from extensions.
|
||||
@@ -386,57 +384,54 @@ export class ConfigurationManager implements IConfigurationManager {
|
||||
return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop();
|
||||
}
|
||||
|
||||
guessDebugger(type?: string): Promise<Debugger | undefined> {
|
||||
async guessDebugger(type?: string): Promise<Debugger | undefined> {
|
||||
if (type) {
|
||||
const adapter = this.getDebugger(type);
|
||||
return Promise.resolve(adapter);
|
||||
}
|
||||
|
||||
const activeTextEditorWidget = this.editorService.activeTextEditorWidget;
|
||||
let candidates: Promise<Debugger[]> | undefined;
|
||||
let candidates: Debugger[] | undefined;
|
||||
if (isCodeEditor(activeTextEditorWidget)) {
|
||||
const model = activeTextEditorWidget.getModel();
|
||||
const language = model ? model.getLanguageIdentifier().language : undefined;
|
||||
const adapters = this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0);
|
||||
if (adapters.length === 1) {
|
||||
return Promise.resolve(adapters[0]);
|
||||
return adapters[0];
|
||||
}
|
||||
if (adapters.length > 1) {
|
||||
candidates = Promise.resolve(adapters);
|
||||
candidates = adapters;
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidates) {
|
||||
candidates = this.activateDebuggers('onDebugInitialConfigurations').then(() => this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider()));
|
||||
await this.activateDebuggers('onDebugInitialConfigurations');
|
||||
candidates = this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider());
|
||||
}
|
||||
|
||||
return candidates.then(debuggers => {
|
||||
debuggers.sort((first, second) => first.label.localeCompare(second.label));
|
||||
const picks = debuggers.map(c => ({ label: c.label, debugger: c }));
|
||||
return this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: 'More...', debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") })
|
||||
.then(picked => {
|
||||
if (picked && picked.debugger) {
|
||||
return picked.debugger;
|
||||
}
|
||||
if (picked) {
|
||||
this.commandService.executeCommand('debug.installAdditionalDebuggers');
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
candidates.sort((first, second) => first.label.localeCompare(second.label));
|
||||
const picks = candidates.map(c => ({ label: c.label, debugger: c }));
|
||||
const picked = await this.quickInputService.pick<{ label: string, debugger: Debugger | undefined }>([...picks, { type: 'separator' }, { label: 'More...', debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") });
|
||||
|
||||
if (picked && picked.debugger) {
|
||||
return picked.debugger;
|
||||
}
|
||||
if (picked) {
|
||||
this.commandService.executeCommand('debug.installAdditionalDebuggers');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
activateDebuggers(activationEvent: string, debugType?: string): Promise<void> {
|
||||
const thenables: Promise<any>[] = [
|
||||
async activateDebuggers(activationEvent: string, debugType?: string): Promise<void> {
|
||||
const promises: Promise<any>[] = [
|
||||
this.extensionService.activateByEvent(activationEvent),
|
||||
this.extensionService.activateByEvent('onDebug')
|
||||
];
|
||||
if (debugType) {
|
||||
thenables.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`));
|
||||
promises.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`));
|
||||
}
|
||||
return Promise.all(thenables).then(_ => {
|
||||
return undefined;
|
||||
});
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
private setSelectedLaunchName(selectedName: string | undefined): void {
|
||||
@@ -533,54 +528,57 @@ class Launch extends AbstractLaunch implements ILaunch {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch', { resource: this.workspace.uri }).workspaceFolder;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditor | null, created: boolean }> {
|
||||
async openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditor | null, created: boolean }> {
|
||||
const resource = this.uri;
|
||||
let created = false;
|
||||
|
||||
return this.fileService.readFile(resource).then(content => content.value, err => {
|
||||
let content = '';
|
||||
try {
|
||||
const fileContent = await this.fileService.readFile(resource);
|
||||
content = fileContent.value.toString();
|
||||
} catch {
|
||||
// launch.json not found: create one by collecting launch configs from debugConfigProviders
|
||||
return this.configurationManager.guessDebugger(type).then(adapter => {
|
||||
if (adapter) {
|
||||
return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type, token || CancellationToken.None).then(initialConfigs => {
|
||||
return adapter.getInitialConfigurationContent(initialConfigs);
|
||||
});
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}).then(content => {
|
||||
|
||||
if (!content) {
|
||||
return '';
|
||||
}
|
||||
const adapter = await this.configurationManager.guessDebugger(type);
|
||||
|
||||
if (adapter) {
|
||||
const initialConfigs = await this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type, token || CancellationToken.None);
|
||||
content = await adapter.getInitialConfigurationContent(initialConfigs);
|
||||
}
|
||||
if (content) {
|
||||
created = true; // pin only if config file is created #8727
|
||||
return this.textFileService.write(resource, content).then(() => content);
|
||||
});
|
||||
}).then(content => {
|
||||
if (!content) {
|
||||
return { editor: null, created: false };
|
||||
}
|
||||
const contentValue = content.toString();
|
||||
const index = contentValue.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
let startLineNumber = 1;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (contentValue.charAt(i) === '\n') {
|
||||
startLineNumber++;
|
||||
try {
|
||||
await this.textFileService.write(resource, content);
|
||||
} catch (error) {
|
||||
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
|
||||
}
|
||||
}
|
||||
const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined;
|
||||
}
|
||||
|
||||
return Promise.resolve(this.editorService.openEditor({
|
||||
resource,
|
||||
options: {
|
||||
selection,
|
||||
preserveFocus,
|
||||
pinned: created,
|
||||
revealIfVisible: true
|
||||
},
|
||||
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created })));
|
||||
}, (error: Error) => {
|
||||
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
|
||||
if (content === '') {
|
||||
return { editor: null, created: false };
|
||||
}
|
||||
|
||||
const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
let startLineNumber = 1;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (content.charAt(i) === '\n') {
|
||||
startLineNumber++;
|
||||
}
|
||||
}
|
||||
const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined;
|
||||
|
||||
const editor = await this.editorService.openEditor({
|
||||
resource,
|
||||
options: {
|
||||
selection,
|
||||
preserveFocus,
|
||||
pinned: created,
|
||||
revealIfVisible: true
|
||||
},
|
||||
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
|
||||
return ({
|
||||
editor: withUndefinedAsNull(editor),
|
||||
created
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -610,11 +608,17 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch').workspace;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> {
|
||||
return this.editorService.openEditor({
|
||||
async openConfigFile(sideBySide: boolean, preserveFocus: boolean): Promise<{ editor: IEditor | null, created: boolean }> {
|
||||
|
||||
const editor = await this.editorService.openEditor({
|
||||
resource: this.contextService.getWorkspace().configuration!,
|
||||
options: { preserveFocus }
|
||||
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created: false }));
|
||||
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
|
||||
return ({
|
||||
editor: withUndefinedAsNull(editor),
|
||||
created: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +651,11 @@ class UserLaunch extends AbstractLaunch implements ILaunch {
|
||||
return this.configurationService.inspect<IGlobalConfig>('launch').user;
|
||||
}
|
||||
|
||||
openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> {
|
||||
return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor: withUndefinedAsNull(editor), created: false }));
|
||||
async openConfigFile(_: boolean, preserveFocus: boolean): Promise<{ editor: IEditor | null, created: boolean }> {
|
||||
const editor = await this.preferencesService.openGlobalSettings(false, { preserveFocus });
|
||||
return ({
|
||||
editor: withUndefinedAsNull(editor),
|
||||
created: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class ToggleBreakpointAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
|
||||
if (editor.hasModel()) {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const modelUri = editor.getModel().uri;
|
||||
@@ -49,12 +49,10 @@ class ToggleBreakpointAction extends EditorAction {
|
||||
} else if (canSet) {
|
||||
return (debugService.addBreakpoints(modelUri, [{ lineNumber: line }], 'debugEditorActions.toggleBreakpointAction'));
|
||||
} else {
|
||||
return Promise.resolve([]);
|
||||
return [];
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +73,7 @@ class ConditionalBreakpointAction extends EditorAction {
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined);
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined, BreakpointWidgetContext.CONDITION);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,15 +95,15 @@ class LogPointAction extends EditorAction {
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, BreakpointWidgetContext.LOG_MESSAGE);
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RunToCursorAction extends EditorAction {
|
||||
|
||||
public static ID = 'editor.debug.action.runToCursor';
|
||||
public static LABEL = nls.localize('runToCursor', "Run to Cursor");
|
||||
public static readonly ID = 'editor.debug.action.runToCursor';
|
||||
public static readonly LABEL = nls.localize('runToCursor', "Run to Cursor");
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -120,11 +118,11 @@ export class RunToCursorAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const focusedSession = debugService.getViewModel().focusedSession;
|
||||
if (debugService.state !== State.Stopped || !focusedSession) {
|
||||
return Promise.resolve(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
let breakpointToRemove: IBreakpoint;
|
||||
@@ -139,18 +137,18 @@ export class RunToCursorAction extends EditorAction {
|
||||
});
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (!editor.hasModel() || !position) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const uri = editor.getModel().uri;
|
||||
const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length);
|
||||
return (bpExists ? Promise.resolve(null) : <Promise<any>>debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }], 'debugEditorActions.runToCursorAction')).then((breakpoints) => {
|
||||
if (breakpoints && breakpoints.length) {
|
||||
breakpointToRemove = breakpoints[0];
|
||||
if (editor.hasModel() && position) {
|
||||
const uri = editor.getModel().uri;
|
||||
const bpExists = !!(debugService.getModel().getBreakpoints({ column: position.column, lineNumber: position.lineNumber, uri }).length);
|
||||
if (!bpExists) {
|
||||
const breakpoints = await debugService.addBreakpoints(uri, [{ lineNumber: position.lineNumber, column: position.column }], 'debugEditorActions.runToCursorAction');
|
||||
if (breakpoints && breakpoints.length) {
|
||||
breakpointToRemove = breakpoints[0];
|
||||
}
|
||||
}
|
||||
debugService.getViewModel().focusedThread!.continue();
|
||||
});
|
||||
|
||||
await debugService.getViewModel().focusedThread!.continue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,13 +167,13 @@ class SelectionToReplAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const panelService = accessor.get(IPanelService);
|
||||
const viewModel = debugService.getViewModel();
|
||||
const session = viewModel.focusedSession;
|
||||
if (!editor.hasModel() || !session) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const text = editor.getModel().getValueInRange(editor.getSelection());
|
||||
@@ -199,15 +197,16 @@ class SelectionToWatchExpressionsAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
if (!editor.hasModel()) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const text = editor.getModel().getValueInRange(editor.getSelection());
|
||||
return viewletService.openViewlet(VIEWLET_ID).then(() => debugService.addWatchExpression(text));
|
||||
await viewletService.openViewlet(VIEWLET_ID);
|
||||
debugService.addWatchExpression(text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,14 +226,14 @@ class ShowDebugHoverAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
|
||||
const position = editor.getPosition();
|
||||
if (!position || !editor.hasModel()) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
const word = editor.getModel().getWordAtPosition(position);
|
||||
if (!word) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const range = new Range(position.lineNumber, position.column, position.lineNumber, word.endColumn);
|
||||
@@ -247,7 +246,7 @@ class GoToBreakpointAction extends EditorAction {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<any> {
|
||||
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
|
||||
const debugService = accessor.get(IDebugService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
if (editor.hasModel()) {
|
||||
@@ -279,8 +278,6 @@ class GoToBreakpointAction extends EditorAction {
|
||||
return openBreakpointSource(moveBreakpoint, false, true, debugService, editorService);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { visit } from 'vs/base/common/json';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
@@ -98,7 +98,7 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
this.wordToLineNumbersMap = undefined;
|
||||
this.updateInlineValuesScheduler.schedule();
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidChangeModel(() => {
|
||||
this.toDispose.push(this.editor.onDidChangeModel(async () => {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
@@ -108,7 +108,7 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
this.hideHoverWidget();
|
||||
this.updateConfigurationWidgetVisibility();
|
||||
this.wordToLineNumbersMap = undefined;
|
||||
this.updateInlineValueDecorations(stackFrame);
|
||||
await this.updateInlineValueDecorations(stackFrame);
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidScrollChange(() => this.hideHoverWidget));
|
||||
this.toDispose.push(this.debugService.onDidChangeState((state: State) => {
|
||||
@@ -141,32 +141,26 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return EDITOR_CONTRIBUTION_ID;
|
||||
}
|
||||
|
||||
showHover(range: Range, focus: boolean): Promise<void> {
|
||||
async showHover(range: Range, focus: boolean): Promise<void> {
|
||||
const sf = this.debugService.getViewModel().focusedStackFrame;
|
||||
const model = this.editor.getModel();
|
||||
if (sf && model && sf.source.uri.toString() === model.uri.toString()) {
|
||||
return this.hoverWidget.showAt(range, focus);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private onFocusStackFrame(sf: IStackFrame | undefined): void {
|
||||
private async onFocusStackFrame(sf: IStackFrame | undefined): Promise<void> {
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
this._applyHoverConfiguration(model, sf);
|
||||
if (sf && sf.source.uri.toString() === model.uri.toString()) {
|
||||
this.toggleExceptionWidget();
|
||||
await this.toggleExceptionWidget();
|
||||
} else {
|
||||
this.hideHoverWidget();
|
||||
}
|
||||
}
|
||||
|
||||
this.updateInlineValueDecorations(sf);
|
||||
await this.updateInlineValueDecorations(sf);
|
||||
}
|
||||
|
||||
@memoize
|
||||
@@ -261,7 +255,7 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
// end hover business
|
||||
|
||||
// exception widget
|
||||
private toggleExceptionWidget(): void {
|
||||
private async toggleExceptionWidget(): Promise<void> {
|
||||
// Toggles exception widget based on the state of the current editor model and debug stack frame
|
||||
const model = this.editor.getModel();
|
||||
const focusedSf = this.debugService.getViewModel().focusedStackFrame;
|
||||
@@ -282,11 +276,10 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
if (this.exceptionWidget && !sameUri) {
|
||||
this.closeExceptionWidget();
|
||||
} else if (sameUri) {
|
||||
focusedSf.thread.exceptionInfo.then(exceptionInfo => {
|
||||
if (exceptionInfo && exceptionSf.range.startLineNumber && exceptionSf.range.startColumn) {
|
||||
this.showExceptionWidget(exceptionInfo, this.debugService.getViewModel().focusedSession, exceptionSf.range.startLineNumber, exceptionSf.range.startColumn);
|
||||
}
|
||||
});
|
||||
const exceptionInfo = await focusedSf.thread.exceptionInfo;
|
||||
if (exceptionInfo && exceptionSf.range.startLineNumber && exceptionSf.range.startColumn) {
|
||||
this.showExceptionWidget(exceptionInfo, this.debugService.getViewModel().focusedSession, exceptionSf.range.startLineNumber, exceptionSf.range.startColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +313,7 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
addLaunchConfiguration(): Promise<any> {
|
||||
async addLaunchConfiguration(): Promise<any> {
|
||||
/* __GDPR__
|
||||
"debug/addLaunchConfiguration" : {}
|
||||
*/
|
||||
@@ -328,7 +321,7 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
let configurationsArrayPosition: Position | undefined;
|
||||
const model = this.editor.getModel();
|
||||
if (!model) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let depthInArray = 0;
|
||||
@@ -351,7 +344,7 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
this.editor.focus();
|
||||
if (!configurationsArrayPosition) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const insertLine = (position: Position): Promise<any> => {
|
||||
@@ -364,7 +357,8 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return this.commandService.executeCommand('editor.action.insertLineAfter');
|
||||
};
|
||||
|
||||
return insertLine(configurationsArrayPosition).then(() => this.commandService.executeCommand('editor.action.triggerSuggest'));
|
||||
await insertLine(configurationsArrayPosition);
|
||||
await this.commandService.executeCommand('editor.action.triggerSuggest');
|
||||
}
|
||||
|
||||
// Inline Decorations
|
||||
@@ -380,12 +374,12 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
@memoize
|
||||
private get updateInlineValuesScheduler(): RunOnceScheduler {
|
||||
return new RunOnceScheduler(
|
||||
() => this.updateInlineValueDecorations(this.debugService.getViewModel().focusedStackFrame),
|
||||
async () => await this.updateInlineValueDecorations(this.debugService.getViewModel().focusedStackFrame),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
private updateInlineValueDecorations(stackFrame: IStackFrame | undefined): void {
|
||||
private async updateInlineValueDecorations(stackFrame: IStackFrame | undefined): Promise<void> {
|
||||
const model = this.editor.getModel();
|
||||
if (!this.configurationService.getValue<IDebugConfiguration>('debug').inlineValues ||
|
||||
!model || !stackFrame || model.uri.toString() !== stackFrame.source.uri.toString()) {
|
||||
@@ -397,20 +391,20 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
this.removeInlineValuesScheduler.cancel();
|
||||
|
||||
stackFrame.getMostSpecificScopes(stackFrame.range)
|
||||
// Get all top level children in the scope chain
|
||||
.then(scopes => Promise.all(scopes.map(scope => scope.getChildren()
|
||||
.then(children => {
|
||||
let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn);
|
||||
if (scope.range) {
|
||||
range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn);
|
||||
}
|
||||
const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range);
|
||||
// Get all top level children in the scope chain
|
||||
const decorationsPerScope = await Promise.all(scopes.map(async scope => {
|
||||
const children = await scope.getChildren();
|
||||
let range = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn);
|
||||
if (scope.range) {
|
||||
range = range.setStartPosition(scope.range.startLineNumber, scope.range.startColumn);
|
||||
}
|
||||
|
||||
return this.createInlineValueDecorationsInsideRange(children, range, model);
|
||||
}))).then(decorationsPerScope => {
|
||||
const allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []);
|
||||
this.editor.setDecorations(INLINE_VALUE_DECORATION_KEY, allDecorations);
|
||||
}));
|
||||
return this.createInlineValueDecorationsInsideRange(children, range, model);
|
||||
}));
|
||||
|
||||
const allDecorations = decorationsPerScope.reduce((previous, current) => previous.concat(current), []);
|
||||
this.editor.setDecorations(INLINE_VALUE_DECORATION_KEY, allDecorations);
|
||||
}
|
||||
|
||||
private createInlineValueDecorationsInsideRange(expressions: ReadonlyArray<IExpression>, range: Range, model: ITextModel): IDecorationOptions[] {
|
||||
@@ -547,4 +541,4 @@ class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(DebugEditorContribution);
|
||||
registerEditorContribution(EDITOR_CONTRIBUTION_ID, DebugEditorContribution);
|
||||
|
||||
@@ -94,12 +94,12 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
if (colors.editorHoverBackground) {
|
||||
this.domNode.style.backgroundColor = colors.editorHoverBackground.toString();
|
||||
} else {
|
||||
this.domNode.style.backgroundColor = null;
|
||||
this.domNode.style.backgroundColor = '';
|
||||
}
|
||||
if (colors.editorHoverBorder) {
|
||||
this.domNode.style.border = `1px solid ${colors.editorHoverBorder}`;
|
||||
} else {
|
||||
this.domNode.style.border = null;
|
||||
this.domNode.style.border = '';
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.tree.onDidChangeContentHeight(() => this.layoutTreeAndContainer()));
|
||||
@@ -174,7 +174,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
return this.doShow(pos, expression, focus);
|
||||
}
|
||||
|
||||
private static _HOVER_HIGHLIGHT_DECORATION_OPTIONS = ModelDecorationOptions.register({
|
||||
private static readonly _HOVER_HIGHLIGHT_DECORATION_OPTIONS = ModelDecorationOptions.register({
|
||||
className: 'hoverHighlight'
|
||||
});
|
||||
|
||||
|
||||
@@ -24,14 +24,13 @@ import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager';
|
||||
import Constants from 'vs/workbench/contrib/markers/browser/constants';
|
||||
import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService';
|
||||
import { TaskError } from 'vs/workbench/contrib/tasks/common/taskSystem';
|
||||
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { parse, getFirstFrame } from 'vs/base/common/console';
|
||||
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
|
||||
@@ -121,7 +120,7 @@ export class DebugService implements IDebugService {
|
||||
this._onWillNewSession = new Emitter<IDebugSession>();
|
||||
this._onDidEndSession = new Emitter<IDebugSession>();
|
||||
|
||||
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this);
|
||||
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
|
||||
this.toDispose.push(this.configurationManager);
|
||||
|
||||
this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
|
||||
@@ -144,13 +143,13 @@ export class DebugService implements IDebugService {
|
||||
session.configuration.request = 'attach';
|
||||
session.configuration.port = event.port;
|
||||
session.setSubId(event.subId);
|
||||
this.launchOrAttachToSession(session).then(undefined, errors.onUnexpectedError);
|
||||
this.launchOrAttachToSession(session);
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.extensionHostDebugService.onTerminateSession(event => {
|
||||
const session = this.model.getSession(event.sessionId);
|
||||
if (session && session.subId === event.subId) {
|
||||
session.disconnect().then(undefined, errors.onUnexpectedError);
|
||||
session.disconnect();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.extensionHostDebugService.onLogToSession(event => {
|
||||
@@ -253,96 +252,99 @@ export class DebugService implements IDebugService {
|
||||
* main entry point
|
||||
* properly manages compounds, checks for errors and handles the initializing state.
|
||||
*/
|
||||
startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise<boolean> {
|
||||
async startDebugging(launch: ILaunch | undefined, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise<boolean> {
|
||||
|
||||
this.startInitializingState();
|
||||
// make sure to save all files and that the configuration is up to date
|
||||
return this.extensionService.activateByEvent('onDebug').then(() => {
|
||||
return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => {
|
||||
return this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
try {
|
||||
// make sure to save all files and that the configuration is up to date
|
||||
await this.extensionService.activateByEvent('onDebug');
|
||||
await this.textFileService.saveAll();
|
||||
await this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined);
|
||||
await this.extensionService.whenInstalledExtensionsRegistered();
|
||||
|
||||
let config: IConfig | undefined;
|
||||
let compound: ICompound | undefined;
|
||||
if (!configOrName) {
|
||||
configOrName = this.configurationManager.selectedConfiguration.name;
|
||||
let config: IConfig | undefined;
|
||||
let compound: ICompound | undefined;
|
||||
if (!configOrName) {
|
||||
configOrName = this.configurationManager.selectedConfiguration.name;
|
||||
}
|
||||
if (typeof configOrName === 'string' && launch) {
|
||||
config = launch.getConfiguration(configOrName);
|
||||
compound = launch.getCompound(configOrName);
|
||||
|
||||
const sessions = this.model.getSessions();
|
||||
const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName);
|
||||
if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || s.root.uri.toString() === launch.workspace.uri.toString()))) {
|
||||
throw new Error(alreadyRunningMessage);
|
||||
}
|
||||
if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) {
|
||||
throw new Error(alreadyRunningMessage);
|
||||
}
|
||||
} else if (typeof configOrName !== 'string') {
|
||||
config = configOrName;
|
||||
}
|
||||
|
||||
if (compound) {
|
||||
// we are starting a compound debug, first do some error checking and than start each configuration in the compound
|
||||
if (!compound.configurations) {
|
||||
throw new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] },
|
||||
"Compound must have \"configurations\" attribute set in order to start multiple configurations."));
|
||||
}
|
||||
|
||||
const values = await Promise.all(compound.configurations.map(configData => {
|
||||
const name = typeof configData === 'string' ? configData : configData.name;
|
||||
if (name === compound!.name) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (typeof configOrName === 'string' && launch) {
|
||||
config = launch.getConfiguration(configOrName);
|
||||
compound = launch.getCompound(configOrName);
|
||||
|
||||
const sessions = this.model.getSessions();
|
||||
const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName);
|
||||
if (sessions.some(s => s.configuration.name === configOrName && (!launch || !launch.workspace || !s.root || s.root.uri.toString() === launch.workspace.uri.toString()))) {
|
||||
return Promise.reject(new Error(alreadyRunningMessage));
|
||||
let launchForName: ILaunch | undefined;
|
||||
if (typeof configData === 'string') {
|
||||
const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name));
|
||||
if (launchesContainingName.length === 1) {
|
||||
launchForName = launchesContainingName[0];
|
||||
} else if (launch && 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 {
|
||||
throw 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));
|
||||
}
|
||||
if (compound && compound.configurations && sessions.some(p => compound!.configurations.indexOf(p.configuration.name) !== -1)) {
|
||||
return Promise.reject(new Error(alreadyRunningMessage));
|
||||
} 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 {
|
||||
throw new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound!.name));
|
||||
}
|
||||
} else if (typeof configOrName !== 'string') {
|
||||
config = configOrName;
|
||||
}
|
||||
|
||||
if (compound) {
|
||||
// we are starting a compound debug, first do some error checking and than start each configuration in the compound
|
||||
if (!compound.configurations) {
|
||||
return Promise.reject(new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] },
|
||||
"Compound must have \"configurations\" attribute set in order to start multiple configurations.")));
|
||||
}
|
||||
return this.createSession(launchForName, launchForName!.getConfiguration(name), options);
|
||||
}));
|
||||
|
||||
return Promise.all(compound.configurations.map(configData => {
|
||||
const name = typeof configData === 'string' ? configData : configData.name;
|
||||
if (name === compound!.name) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const result = values.every(success => !!success); // Compound launch is a success only if each configuration launched successfully
|
||||
this.endInitializingState();
|
||||
return result;
|
||||
}
|
||||
|
||||
let launchForName: ILaunch | undefined;
|
||||
if (typeof configData === 'string') {
|
||||
const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name));
|
||||
if (launchesContainingName.length === 1) {
|
||||
launchForName = launchesContainingName[0];
|
||||
} else if (launch && 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 Promise.reject(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 Promise.reject(new Error(nls.localize('noFolderWithName', "Can not find folder with name '{0}' for configuration '{1}' in compound '{2}'.", configData.folder, configData.name, compound!.name)));
|
||||
}
|
||||
}
|
||||
if (configOrName && !config) {
|
||||
const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : JSON.stringify(configOrName)) :
|
||||
nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist.");
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return this.createSession(launchForName, launchForName!.getConfiguration(name), options);
|
||||
})).then(values => values.every(success => !!success)); // Compound launch is a success only if each configuration launched successfully
|
||||
}
|
||||
|
||||
if (configOrName && !config) {
|
||||
const message = !!launch ? nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", typeof configOrName === 'string' ? configOrName : JSON.stringify(configOrName)) :
|
||||
nls.localize('launchJsonDoesNotExist', "'launch.json' does not exist.");
|
||||
return Promise.reject(new Error(message));
|
||||
}
|
||||
|
||||
return this.createSession(launch, config, options);
|
||||
});
|
||||
}));
|
||||
}).then(success => {
|
||||
const result = await this.createSession(launch, config, options);
|
||||
this.endInitializingState();
|
||||
return result;
|
||||
} catch (err) {
|
||||
// make sure to get out of initializing state, and propagate the result
|
||||
this.endInitializingState();
|
||||
return success;
|
||||
}, err => {
|
||||
this.endInitializingState();
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the debugger for the type, resolves configurations by providers, substitutes variables and runs prelaunch tasks
|
||||
*/
|
||||
private createSession(launch: ILaunch | undefined, config: IConfig | undefined, options?: IDebugSessionOptions): Promise<boolean> {
|
||||
private async createSession(launch: ILaunch | undefined, config: IConfig | undefined, options?: IDebugSessionOptions): Promise<boolean> {
|
||||
// We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes.
|
||||
// Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config.
|
||||
let type: string | undefined;
|
||||
@@ -358,66 +360,71 @@ export class DebugService implements IDebugService {
|
||||
config!.noDebug = true;
|
||||
}
|
||||
|
||||
const debuggerThenable: Promise<void> = type ? Promise.resolve() : this.configurationManager.guessDebugger().then(dbgr => { type = dbgr && dbgr.type; });
|
||||
return debuggerThenable.then(() => {
|
||||
this.initCancellationToken = new CancellationTokenSource();
|
||||
return this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config!, this.initCancellationToken.token).then(config => {
|
||||
// a falsy config indicates an aborted launch
|
||||
if (config && config.type) {
|
||||
return this.substituteVariables(launch, config).then(resolvedConfig => {
|
||||
if (!type) {
|
||||
const guess = await this.configurationManager.guessDebugger();
|
||||
if (guess) {
|
||||
type = guess.type;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolvedConfig) {
|
||||
// User canceled resolving of interactive variables, silently return
|
||||
return false;
|
||||
}
|
||||
this.initCancellationToken = new CancellationTokenSource();
|
||||
const configByProviders = await this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config!, this.initCancellationToken.token);
|
||||
// a falsy config indicates an aborted launch
|
||||
if (configByProviders && configByProviders.type) {
|
||||
try {
|
||||
const resolvedConfig = await this.substituteVariables(launch, configByProviders);
|
||||
|
||||
if (!this.configurationManager.getDebugger(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)
|
||||
: 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.");
|
||||
}
|
||||
|
||||
return this.showError(message).then(() => false);
|
||||
}
|
||||
|
||||
const workspace = launch ? launch.workspace : undefined;
|
||||
return this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask).then(result => {
|
||||
if (result === TaskRunResult.Success) {
|
||||
return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, err => {
|
||||
if (err && err.message) {
|
||||
return this.showError(err.message).then(() => false);
|
||||
}
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved and that you have a debug extension installed for that file type."))
|
||||
.then(() => false);
|
||||
}
|
||||
|
||||
return !!launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false);
|
||||
});
|
||||
if (!resolvedConfig) {
|
||||
// User canceled resolving of interactive variables, silently return
|
||||
return false;
|
||||
}
|
||||
|
||||
if (launch && type && config === null) { // show launch.json only for "config" being "null".
|
||||
return launch.openConfigFile(false, true, type, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false);
|
||||
if (!this.configurationManager.getDebugger(resolvedConfig.type) || (configByProviders.request !== 'attach' && configByProviders.request !== 'launch')) {
|
||||
let message: string;
|
||||
if (configByProviders.request !== 'attach' && configByProviders.request !== 'launch') {
|
||||
message = configByProviders.request ? nls.localize('debugRequestNotSupported', "Attribute '{0}' has an unsupported value '{1}' in the chosen debug configuration.", 'request', configByProviders.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.");
|
||||
}
|
||||
|
||||
await this.showError(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const workspace = launch ? launch.workspace : undefined;
|
||||
const taskResult = await this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask);
|
||||
if (taskResult === TaskRunResult.Success) {
|
||||
return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options);
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
if (err && err.message) {
|
||||
await this.showError(err.message);
|
||||
} else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
await this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved and that you have a debug extension installed for that file type."));
|
||||
}
|
||||
if (launch) {
|
||||
await launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (launch && type && configByProviders === null) { // show launch.json only for "config" being "null".
|
||||
await launch.openConfigFile(false, true, type, this.initCancellationToken ? this.initCancellationToken.token : undefined);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* instantiates the new session, initializes the session, registers session listeners and reports telemetry
|
||||
*/
|
||||
private doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }, options?: IDebugSessionOptions): Promise<boolean> {
|
||||
private async doCreateSession(root: IWorkspaceFolder | undefined, configuration: { resolved: IConfig, unresolved: IConfig | undefined }, options?: IDebugSessionOptions): Promise<boolean> {
|
||||
|
||||
const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model, options);
|
||||
this.model.addSession(session);
|
||||
@@ -431,10 +438,11 @@ export class DebugService implements IDebugService {
|
||||
const openDebug = this.configurationService.getValue<IDebugConfiguration>('debug').openDebug;
|
||||
// Open debug viewlet based on the visibility of the side bar and openDebug setting. Do not open for 'run without debug'
|
||||
if (!configuration.resolved.noDebug && (openDebug === 'openOnSessionStart' || (openDebug === 'openOnFirstSessionStart' && this.viewModel.firstSessionStart))) {
|
||||
this.viewletService.openViewlet(VIEWLET_ID).then(undefined, errors.onUnexpectedError);
|
||||
await this.viewletService.openViewlet(VIEWLET_ID);
|
||||
}
|
||||
|
||||
return this.launchOrAttachToSession(session).then(() => {
|
||||
try {
|
||||
await this.launchOrAttachToSession(session);
|
||||
|
||||
const internalConsoleOptions = session.configuration.internalConsoleOptions || this.configurationService.getValue<IDebugConfiguration>('debug').internalConsoleOptions;
|
||||
if (internalConsoleOptions === 'openOnSessionStart' || (this.viewModel.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) {
|
||||
@@ -452,12 +460,14 @@ export class DebugService implements IDebugService {
|
||||
// since the initialized response has arrived announce the new Session (including extensions)
|
||||
this._onDidNewSession.fire(session);
|
||||
|
||||
return this.telemetryDebugSessionStart(root, session.configuration.type);
|
||||
}).then(() => true, (error: Error | string) => {
|
||||
await this.telemetryDebugSessionStart(root, session.configuration.type);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
if (errors.isPromiseCanceledError(error)) {
|
||||
// don't show 'canceled' error messages to the user #7906
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Show the repl if some error got logged there #5870
|
||||
@@ -467,27 +477,29 @@ export class DebugService implements IDebugService {
|
||||
|
||||
if (session.configuration && session.configuration.request === 'attach' && session.configuration.__autoAttach) {
|
||||
// ignore attach timeouts in auto attach mode
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : error;
|
||||
this.telemetryDebugMisconfiguration(session.configuration ? session.configuration.type : undefined, errorMessage);
|
||||
return this.showError(errorMessage, isErrorWithActions(error) ? error.actions : []).then(() => false);
|
||||
});
|
||||
|
||||
await this.showError(errorMessage, isErrorWithActions(error) ? error.actions : []);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise<void> {
|
||||
private async launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise<void> {
|
||||
const dbgr = this.configurationManager.getDebugger(session.configuration.type);
|
||||
return session.initialize(dbgr!).then(() => {
|
||||
return session.launchOrAttach(session.configuration).then(() => {
|
||||
if (forceFocus || !this.viewModel.focusedSession) {
|
||||
this.focusStackFrame(undefined, undefined, session);
|
||||
}
|
||||
});
|
||||
}).then(undefined, err => {
|
||||
try {
|
||||
await session.initialize(dbgr!);
|
||||
await session.launchOrAttach(session.configuration);
|
||||
if (forceFocus || !this.viewModel.focusedSession) {
|
||||
await this.focusStackFrame(undefined, undefined, session);
|
||||
}
|
||||
} catch (err) {
|
||||
session.shutdown();
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private registerSessionListeners(session: IDebugSession): void {
|
||||
@@ -506,7 +518,7 @@ export class DebugService implements IDebugService {
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(session.onDidEndAdapter(adapterExitEvent => {
|
||||
this.toDispose.push(session.onDidEndAdapter(async adapterExitEvent => {
|
||||
|
||||
if (adapterExitEvent.error) {
|
||||
this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly ({0})", adapterExitEvent.error.message || adapterExitEvent.error.toString()));
|
||||
@@ -520,9 +532,11 @@ export class DebugService implements IDebugService {
|
||||
this.telemetryDebugSessionStop(session, adapterExitEvent);
|
||||
|
||||
if (session.configuration.postDebugTask) {
|
||||
this.runTask(session.root, session.configuration.postDebugTask).then(undefined, err =>
|
||||
this.notificationService.error(err)
|
||||
);
|
||||
try {
|
||||
await this.runTask(session.root, session.configuration.postDebugTask);
|
||||
} catch (err) {
|
||||
this.notificationService.error(err);
|
||||
}
|
||||
}
|
||||
session.shutdown();
|
||||
this.endInitializingState();
|
||||
@@ -530,7 +544,7 @@ export class DebugService implements IDebugService {
|
||||
|
||||
const focusedSession = this.viewModel.focusedSession;
|
||||
if (focusedSession && focusedSession.getId() === session.getId()) {
|
||||
this.focusStackFrame(undefined);
|
||||
await this.focusStackFrame(undefined);
|
||||
}
|
||||
|
||||
if (this.model.getSessions().length === 0) {
|
||||
@@ -548,87 +562,93 @@ export class DebugService implements IDebugService {
|
||||
}));
|
||||
}
|
||||
|
||||
restartSession(session: IDebugSession, restartData?: any): Promise<any> {
|
||||
return this.textFileService.saveAll().then(() => {
|
||||
const isAutoRestart = !!restartData;
|
||||
const runTasks: () => Promise<TaskRunResult> = () => {
|
||||
if (isAutoRestart) {
|
||||
// Do not run preLaunch and postDebug tasks for automatic restarts
|
||||
return Promise.resolve(TaskRunResult.Success);
|
||||
async restartSession(session: IDebugSession, restartData?: any): Promise<any> {
|
||||
await this.textFileService.saveAll();
|
||||
const isAutoRestart = !!restartData;
|
||||
|
||||
const runTasks: () => Promise<TaskRunResult> = async () => {
|
||||
if (isAutoRestart) {
|
||||
// Do not run preLaunch and postDebug tasks for automatic restarts
|
||||
return Promise.resolve(TaskRunResult.Success);
|
||||
}
|
||||
|
||||
await this.runTask(session.root, session.configuration.postDebugTask);
|
||||
return this.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask);
|
||||
};
|
||||
|
||||
if (session.capabilities.supportsRestartRequest) {
|
||||
const taskResult = await runTasks();
|
||||
if (taskResult === TaskRunResult.Success) {
|
||||
await session.restart();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExtensionHostDebugging(session.configuration)) {
|
||||
const taskResult = await runTasks();
|
||||
if (taskResult === TaskRunResult.Success) {
|
||||
this.extensionHostDebugService.reload(session.getId());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
|
||||
// If the restart is automatic -> disconnect, otherwise -> terminate #55064
|
||||
if (isAutoRestart) {
|
||||
await session.disconnect(true);
|
||||
} else {
|
||||
await session.terminate(true);
|
||||
}
|
||||
|
||||
return new Promise<void>((c, e) => {
|
||||
setTimeout(async () => {
|
||||
const taskResult = await runTasks();
|
||||
if (taskResult !== TaskRunResult.Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.runTask(session.root, session.configuration.postDebugTask)
|
||||
.then(() => this.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask));
|
||||
};
|
||||
// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
|
||||
let needsToSubstitute = false;
|
||||
let unresolved: IConfig | undefined;
|
||||
const launch = session.root ? this.configurationManager.getLaunch(session.root.uri) : undefined;
|
||||
if (launch) {
|
||||
unresolved = launch.getConfiguration(session.configuration.name);
|
||||
if (unresolved && !equals(unresolved, session.unresolvedConfiguration)) {
|
||||
// Take the type from the session since the debug extension might overwrite it #21316
|
||||
unresolved.type = session.configuration.type;
|
||||
unresolved.noDebug = session.configuration.noDebug;
|
||||
needsToSubstitute = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (session.capabilities.supportsRestartRequest) {
|
||||
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? session.restart() : undefined);
|
||||
}
|
||||
let resolved: IConfig | undefined | null = session.configuration;
|
||||
if (launch && needsToSubstitute && unresolved) {
|
||||
this.initCancellationToken = new CancellationTokenSource();
|
||||
const resolvedByProviders = await this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved, this.initCancellationToken.token);
|
||||
if (resolvedByProviders) {
|
||||
resolved = await this.substituteVariables(launch, resolvedByProviders);
|
||||
} else {
|
||||
resolved = resolvedByProviders;
|
||||
}
|
||||
}
|
||||
|
||||
if (isExtensionHostDebugging(session.configuration)) {
|
||||
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined);
|
||||
}
|
||||
if (!resolved) {
|
||||
return c(undefined);
|
||||
}
|
||||
|
||||
const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
|
||||
// If the restart is automatic -> disconnect, otherwise -> terminate #55064
|
||||
return (isAutoRestart ? session.disconnect(true) : session.terminate(true)).then(() => {
|
||||
session.setConfiguration({ resolved, unresolved });
|
||||
session.configuration.__restart = restartData;
|
||||
|
||||
return new Promise<void>((c, e) => {
|
||||
setTimeout(() => {
|
||||
runTasks().then(taskResult => {
|
||||
if (taskResult !== TaskRunResult.Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
|
||||
let needsToSubstitute = false;
|
||||
let unresolved: IConfig | undefined;
|
||||
const launch = session.root ? this.configurationManager.getLaunch(session.root.uri) : undefined;
|
||||
if (launch) {
|
||||
unresolved = launch.getConfiguration(session.configuration.name);
|
||||
if (unresolved && !equals(unresolved, session.unresolvedConfiguration)) {
|
||||
// Take the type from the session since the debug extension might overwrite it #21316
|
||||
unresolved.type = session.configuration.type;
|
||||
unresolved.noDebug = session.configuration.noDebug;
|
||||
needsToSubstitute = true;
|
||||
}
|
||||
}
|
||||
|
||||
let substitutionThenable: Promise<IConfig | null | undefined> = Promise.resolve(session.configuration);
|
||||
if (launch && needsToSubstitute && unresolved) {
|
||||
this.initCancellationToken = new CancellationTokenSource();
|
||||
substitutionThenable = this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved, this.initCancellationToken.token)
|
||||
.then(resolved => {
|
||||
if (resolved) {
|
||||
// start debugging
|
||||
return this.substituteVariables(launch, resolved);
|
||||
} else if (resolved === null) {
|
||||
// abort debugging silently and open launch.json
|
||||
return Promise.resolve(null);
|
||||
} else {
|
||||
// abort debugging silently
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
substitutionThenable.then(resolved => {
|
||||
|
||||
if (!resolved) {
|
||||
return c(undefined);
|
||||
}
|
||||
|
||||
session.setConfiguration({ resolved, unresolved });
|
||||
session.configuration.__restart = restartData;
|
||||
|
||||
this.launchOrAttachToSession(session, shouldFocus).then(() => {
|
||||
this._onDidNewSession.fire(session);
|
||||
c(undefined);
|
||||
}, err => e(err));
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
try {
|
||||
await this.launchOrAttachToSession(session, shouldFocus);
|
||||
this._onDidNewSession.fire(session);
|
||||
c(undefined);
|
||||
} catch (error) {
|
||||
e(error);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -646,7 +666,7 @@ export class DebugService implements IDebugService {
|
||||
return Promise.all(sessions.map(s => s.terminate()));
|
||||
}
|
||||
|
||||
private substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise<IConfig | undefined> {
|
||||
private async substituteVariables(launch: ILaunch | undefined, config: IConfig): Promise<IConfig | undefined> {
|
||||
const dbg = this.configurationManager.getDebugger(config.type);
|
||||
if (dbg) {
|
||||
let folder: IWorkspaceFolder | undefined = undefined;
|
||||
@@ -658,12 +678,12 @@ export class DebugService implements IDebugService {
|
||||
folder = folders[0];
|
||||
}
|
||||
}
|
||||
return dbg.substituteVariables(folder, config).then(config => {
|
||||
return config;
|
||||
}, (err: Error) => {
|
||||
try {
|
||||
return await dbg.substituteVariables(folder, config);
|
||||
} catch (err) {
|
||||
this.showError(err.message);
|
||||
return undefined; // bail out
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve(config);
|
||||
}
|
||||
@@ -681,13 +701,13 @@ export class DebugService implements IDebugService {
|
||||
|
||||
//---- task management
|
||||
|
||||
private runTaskAndCheckErrors(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<TaskRunResult> {
|
||||
|
||||
return this.runTask(root, taskId).then((taskSummary: ITaskSummary) => {
|
||||
private async runTaskAndCheckErrors(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<TaskRunResult> {
|
||||
try {
|
||||
const taskSummary = await this.runTask(root, taskId);
|
||||
|
||||
const errorCount = taskId ? this.markerService.getStatistics().errors : 0;
|
||||
const successExitCode = taskSummary && taskSummary.exitCode === 0;
|
||||
const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
|
||||
const failureExitCode = taskSummary && taskSummary.exitCode !== 0;
|
||||
const onTaskErrors = this.configurationService.getValue<IDebugConfiguration>('debug').onTaskErrors;
|
||||
if (successExitCode || onTaskErrors === 'debugAnyway' || (errorCount === 0 && !failureExitCode)) {
|
||||
return TaskRunResult.Success;
|
||||
@@ -702,34 +722,35 @@ export class DebugService implements IDebugService {
|
||||
? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel)
|
||||
: errorCount === 1
|
||||
? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel)
|
||||
: nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary.exitCode);
|
||||
: nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary ? taskSummary.exitCode : 0);
|
||||
|
||||
return this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], {
|
||||
const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], {
|
||||
checkbox: {
|
||||
label: nls.localize('remember', "Remember my choice in user settings"),
|
||||
},
|
||||
cancelId: 2
|
||||
}).then(result => {
|
||||
if (result.choice === 2) {
|
||||
return Promise.resolve(TaskRunResult.Failure);
|
||||
}
|
||||
const debugAnyway = result.choice === 0;
|
||||
if (result.checkboxChecked) {
|
||||
this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors');
|
||||
}
|
||||
if (debugAnyway) {
|
||||
return TaskRunResult.Success;
|
||||
}
|
||||
|
||||
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
|
||||
return Promise.resolve(TaskRunResult.Failure);
|
||||
});
|
||||
}, (err: TaskError) => {
|
||||
return this.showError(err.message, [this.taskService.configureAction()]);
|
||||
});
|
||||
|
||||
if (result.choice === 2) {
|
||||
return Promise.resolve(TaskRunResult.Failure);
|
||||
}
|
||||
const debugAnyway = result.choice === 0;
|
||||
if (result.checkboxChecked) {
|
||||
this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors');
|
||||
}
|
||||
if (debugAnyway) {
|
||||
return TaskRunResult.Success;
|
||||
}
|
||||
|
||||
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
|
||||
return Promise.resolve(TaskRunResult.Failure);
|
||||
} catch (err) {
|
||||
await this.showError(err.message, [this.taskService.configureAction()]);
|
||||
return TaskRunResult.Failure;
|
||||
}
|
||||
}
|
||||
|
||||
private runTask(root: IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<ITaskSummary | null> {
|
||||
private async runTask(root: IWorkspace | IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<ITaskSummary | null> {
|
||||
if (!taskId) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
@@ -737,58 +758,72 @@ export class DebugService implements IDebugService {
|
||||
return Promise.reject(new Error(nls.localize('invalidTaskReference', "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", typeof taskId === 'string' ? taskId : taskId.type)));
|
||||
}
|
||||
// run a task before starting a debug session
|
||||
return this.taskService.getTask(root, taskId).then(task => {
|
||||
if (!task) {
|
||||
const errorMessage = typeof taskId === 'string'
|
||||
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
|
||||
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
|
||||
return Promise.reject(createErrorWithActions(errorMessage));
|
||||
const task = await this.taskService.getTask(root, taskId);
|
||||
if (!task) {
|
||||
const errorMessage = typeof taskId === 'string'
|
||||
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
|
||||
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
|
||||
return Promise.reject(createErrorWithActions(errorMessage));
|
||||
}
|
||||
|
||||
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
|
||||
let taskStarted = false;
|
||||
const inactivePromise: Promise<ITaskSummary | null> = new Promise((c, e) => once(e => {
|
||||
// When a task isBackground it will go inactive when it is safe to launch.
|
||||
// But when a background task is terminated by the user, it will also fire an inactive event.
|
||||
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
|
||||
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
|
||||
return (e.kind === TaskEventKind.Inactive
|
||||
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
|
||||
&& e.taskId === task._id;
|
||||
}, this.taskService.onDidStateChange)(e => {
|
||||
taskStarted = true;
|
||||
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
|
||||
}));
|
||||
|
||||
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(async (tasks): Promise<ITaskSummary | null> => {
|
||||
if (tasks.filter(t => t._id === task._id).length) {
|
||||
// Check that the task isn't busy and if it is, wait for it
|
||||
const busyTasks = await this.taskService.getBusyTasks();
|
||||
if (busyTasks.filter(t => t._id === task._id).length) {
|
||||
return inactivePromise;
|
||||
}
|
||||
// task is already running and isn't busy - nothing to do.
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
|
||||
// Task is active, so everything seems to be fine, no need to prompt after 10 seconds
|
||||
// Use case being a slow running task should not be prompted even though it takes more than 10 seconds
|
||||
taskStarted = true;
|
||||
});
|
||||
const taskPromise = this.taskService.run(task);
|
||||
if (task.configurationProperties.isBackground) {
|
||||
return inactivePromise;
|
||||
}
|
||||
|
||||
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
|
||||
let taskStarted = false;
|
||||
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(tasks => {
|
||||
if (tasks.filter(t => t._id === task._id).length) {
|
||||
// task is already running - nothing to do.
|
||||
return Promise.resolve(null);
|
||||
return taskPromise;
|
||||
});
|
||||
|
||||
return new Promise((c, e) => {
|
||||
promise.then(result => {
|
||||
taskStarted = true;
|
||||
c(result);
|
||||
}, error => e(error));
|
||||
|
||||
setTimeout(() => {
|
||||
if (!taskStarted) {
|
||||
const errorMessage = typeof taskId === 'string'
|
||||
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
|
||||
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
|
||||
e({ severity: severity.Error, message: errorMessage });
|
||||
}
|
||||
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
|
||||
// Task is active, so everything seems to be fine, no need to prompt after 10 seconds
|
||||
// Use case being a slow running task should not be prompted even though it takes more than 10 seconds
|
||||
taskStarted = true;
|
||||
});
|
||||
const taskPromise = this.taskService.run(task);
|
||||
if (task.configurationProperties.isBackground) {
|
||||
return new Promise((c, e) => once(e => e.kind === TaskEventKind.Inactive && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
|
||||
taskStarted = true;
|
||||
c(null);
|
||||
}));
|
||||
}
|
||||
|
||||
return taskPromise;
|
||||
});
|
||||
|
||||
return new Promise((c, e) => {
|
||||
promise.then(result => {
|
||||
taskStarted = true;
|
||||
c(result);
|
||||
}, error => e(error));
|
||||
|
||||
setTimeout(() => {
|
||||
if (!taskStarted) {
|
||||
const errorMessage = typeof taskId === 'string'
|
||||
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
|
||||
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
|
||||
e({ severity: severity.Error, message: errorMessage });
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
}, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
//---- focus management
|
||||
|
||||
focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): void {
|
||||
async focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise<void> {
|
||||
if (!session) {
|
||||
if (stackFrame || thread) {
|
||||
session = stackFrame ? stackFrame.thread.session : thread!.session;
|
||||
@@ -817,18 +852,17 @@ export class DebugService implements IDebugService {
|
||||
}
|
||||
|
||||
if (stackFrame) {
|
||||
stackFrame.openInEditor(this.editorService, true).then(editor => {
|
||||
if (editor) {
|
||||
const control = editor.getControl();
|
||||
if (stackFrame && isCodeEditor(control) && control.hasModel()) {
|
||||
const model = control.getModel();
|
||||
if (stackFrame.range.startLineNumber <= model.getLineCount()) {
|
||||
const lineContent = control.getModel().getLineContent(stackFrame.range.startLineNumber);
|
||||
aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2} {3}", thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, lineContent));
|
||||
}
|
||||
const editor = await stackFrame.openInEditor(this.editorService, true);
|
||||
if (editor) {
|
||||
const control = editor.getControl();
|
||||
if (stackFrame && isCodeEditor(control) && control.hasModel()) {
|
||||
const model = control.getModel();
|
||||
if (stackFrame.range.startLineNumber <= model.getLineCount()) {
|
||||
const lineContent = control.getModel().getLineContent(stackFrame.range.startLineNumber);
|
||||
aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2} {3}", thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, lineContent));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (session) {
|
||||
this.debugType.set(session.configuration.type);
|
||||
@@ -949,12 +983,12 @@ export class DebugService implements IDebugService {
|
||||
this.storeBreakpoints();
|
||||
}
|
||||
|
||||
sendAllBreakpoints(session?: IDebugSession): Promise<any> {
|
||||
return Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session)))
|
||||
.then(() => this.sendFunctionBreakpoints(session))
|
||||
// send exception breakpoints at the end since some debug adapters rely on the order
|
||||
.then(() => this.sendExceptionBreakpoints(session))
|
||||
.then(() => this.sendDataBreakpoints(session));
|
||||
async sendAllBreakpoints(session?: IDebugSession): Promise<any> {
|
||||
await Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session)));
|
||||
await this.sendFunctionBreakpoints(session);
|
||||
await this.sendDataBreakpoints(session);
|
||||
// send exception breakpoints at the end since some debug adapters rely on the order
|
||||
await this.sendExceptionBreakpoints(session);
|
||||
}
|
||||
|
||||
private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise<void> {
|
||||
@@ -989,11 +1023,12 @@ export class DebugService implements IDebugService {
|
||||
});
|
||||
}
|
||||
|
||||
private sendToOneOrAllSessions(session: IDebugSession | undefined, send: (session: IDebugSession) => Promise<void>): Promise<void> {
|
||||
private async sendToOneOrAllSessions(session: IDebugSession | undefined, send: (session: IDebugSession) => Promise<void>): Promise<void> {
|
||||
if (session) {
|
||||
return send(session);
|
||||
await send(session);
|
||||
} else {
|
||||
await Promise.all(this.model.getSessions().map(s => send(s)));
|
||||
}
|
||||
return Promise.all(this.model.getSessions().map(s => send(s))).then(() => undefined);
|
||||
}
|
||||
|
||||
private onFileChanges(fileChangesEvent: FileChangesEvent): void {
|
||||
|
||||
@@ -30,8 +30,6 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
@@ -65,7 +63,7 @@ export class DebugSession implements IDebugSession {
|
||||
|
||||
constructor(
|
||||
private _configuration: { resolved: IConfig, unresolved: IConfig | undefined },
|
||||
public root: IWorkspaceFolder,
|
||||
public root: IWorkspaceFolder | undefined,
|
||||
private model: DebugModel,
|
||||
options: IDebugSessionOptions | undefined,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@@ -74,7 +72,6 @@ export class DebugSession implements IDebugSession {
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
|
||||
@IOpenerService private readonly openerService: IOpenerService
|
||||
@@ -183,110 +180,100 @@ export class DebugSession implements IDebugSession {
|
||||
/**
|
||||
* create and initialize a new debug adapter for this session
|
||||
*/
|
||||
initialize(dbgr: IDebugger): Promise<void> {
|
||||
async initialize(dbgr: IDebugger): Promise<void> {
|
||||
|
||||
if (this.raw) {
|
||||
// if there was already a connection make sure to remove old listeners
|
||||
this.shutdown();
|
||||
}
|
||||
|
||||
return dbgr.getCustomTelemetryService().then(customTelemetryService => {
|
||||
try {
|
||||
const customTelemetryService = await dbgr.getCustomTelemetryService();
|
||||
const debugAdapter = await dbgr.createDebugAdapter(this);
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService);
|
||||
|
||||
return dbgr.createDebugAdapter(this).then(debugAdapter => {
|
||||
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService);
|
||||
|
||||
return this.raw.start().then(() => {
|
||||
|
||||
this.registerListeners();
|
||||
|
||||
return this.raw!.initialize({
|
||||
clientID: 'vscode',
|
||||
clientName: this.productService.nameLong,
|
||||
adapterID: this.configuration.type,
|
||||
pathFormat: 'path',
|
||||
linesStartAt1: true,
|
||||
columnsStartAt1: true,
|
||||
supportsVariableType: true, // #8858
|
||||
supportsVariablePaging: true, // #9537
|
||||
supportsRunInTerminalRequest: true, // #10574
|
||||
locale: platform.locale
|
||||
}).then(() => {
|
||||
this.initialized = true;
|
||||
this._onDidChangeState.fire();
|
||||
this.model.setExceptionBreakpoints(this.raw!.capabilities.exceptionBreakpointFilters || []);
|
||||
});
|
||||
});
|
||||
await this.raw.start();
|
||||
this.registerListeners();
|
||||
await this.raw!.initialize({
|
||||
clientID: 'vscode',
|
||||
clientName: this.productService.nameLong,
|
||||
adapterID: this.configuration.type,
|
||||
pathFormat: 'path',
|
||||
linesStartAt1: true,
|
||||
columnsStartAt1: true,
|
||||
supportsVariableType: true, // #8858
|
||||
supportsVariablePaging: true, // #9537
|
||||
supportsRunInTerminalRequest: true, // #10574
|
||||
locale: platform.locale
|
||||
});
|
||||
}).then(undefined, err => {
|
||||
|
||||
this.initialized = true;
|
||||
this._onDidChangeState.fire();
|
||||
return Promise.reject(err);
|
||||
});
|
||||
this.model.setExceptionBreakpoints(this.raw!.capabilities.exceptionBreakpointFilters || []);
|
||||
} catch (err) {
|
||||
this.initialized = true;
|
||||
this._onDidChangeState.fire();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* launch or attach to the debuggee
|
||||
*/
|
||||
launchOrAttach(config: IConfig): Promise<void> {
|
||||
if (this.raw) {
|
||||
|
||||
// __sessionID only used for EH debugging (but we add it always for now...)
|
||||
config.__sessionId = this.getId();
|
||||
|
||||
return this.raw.launchOrAttach(config).then(result => {
|
||||
return undefined;
|
||||
});
|
||||
async launchOrAttach(config: IConfig): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
// __sessionID only used for EH debugging (but we add it always for now...)
|
||||
config.__sessionId = this.getId();
|
||||
await this.raw.launchOrAttach(config);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* end the current debug adapter session
|
||||
*/
|
||||
terminate(restart = false): Promise<void> {
|
||||
if (this.raw) {
|
||||
this.cancelAllRequests();
|
||||
if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') {
|
||||
return this.raw.terminate(restart).then(response => {
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return this.raw.disconnect(restart).then(response => {
|
||||
return undefined;
|
||||
});
|
||||
async terminate(restart = false): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
|
||||
this.cancelAllRequests();
|
||||
if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') {
|
||||
await this.raw.terminate(restart);
|
||||
} else {
|
||||
await this.raw.disconnect(restart);
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
/**
|
||||
* end the current debug adapter session
|
||||
*/
|
||||
disconnect(restart = false): Promise<void> {
|
||||
if (this.raw) {
|
||||
this.cancelAllRequests();
|
||||
return this.raw.disconnect(restart).then(response => {
|
||||
return undefined;
|
||||
});
|
||||
async disconnect(restart = false): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
this.cancelAllRequests();
|
||||
await this.raw.disconnect(restart);
|
||||
}
|
||||
|
||||
/**
|
||||
* restart debug adapter session
|
||||
*/
|
||||
restart(): Promise<void> {
|
||||
if (this.raw) {
|
||||
this.cancelAllRequests();
|
||||
return this.raw.restart().then(() => undefined);
|
||||
async restart(): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
this.cancelAllRequests();
|
||||
await this.raw.restart();
|
||||
}
|
||||
|
||||
sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): Promise<void> {
|
||||
|
||||
async sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): Promise<void> {
|
||||
if (!this.raw) {
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
|
||||
if (!this.raw.readyForBreakpoints) {
|
||||
@@ -302,233 +289,252 @@ export class DebugSession implements IDebugSession {
|
||||
rawSource.path = normalizeDriveLetter(rawSource.path);
|
||||
}
|
||||
|
||||
return this.raw.setBreakpoints({
|
||||
const response = await this.raw.setBreakpoints({
|
||||
source: rawSource,
|
||||
lines: breakpointsToSend.map(bp => bp.sessionAgnosticData.lineNumber),
|
||||
breakpoints: breakpointsToSend.map(bp => ({ line: bp.sessionAgnosticData.lineNumber, column: bp.sessionAgnosticData.column, condition: bp.condition, hitCondition: bp.hitCondition, logMessage: bp.logMessage })),
|
||||
sourceModified
|
||||
}).then(response => {
|
||||
});
|
||||
if (response && response.body) {
|
||||
const data = new Map<string, DebugProtocol.Breakpoint>();
|
||||
for (let i = 0; i < breakpointsToSend.length; i++) {
|
||||
data.set(breakpointsToSend[i].getId(), response.body.breakpoints[i]);
|
||||
}
|
||||
|
||||
this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
|
||||
}
|
||||
}
|
||||
|
||||
async sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
const response = await this.raw.setFunctionBreakpoints({ breakpoints: fbpts });
|
||||
if (response && response.body) {
|
||||
const data = new Map<string, DebugProtocol.Breakpoint>();
|
||||
for (let i = 0; i < breakpointsToSend.length; i++) {
|
||||
data.set(breakpointsToSend[i].getId(), response.body.breakpoints[i]);
|
||||
for (let i = 0; i < fbpts.length; i++) {
|
||||
data.set(fbpts[i].getId(), response.body.breakpoints[i]);
|
||||
}
|
||||
|
||||
this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): Promise<void> {
|
||||
if (this.raw) {
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
return this.raw.setFunctionBreakpoints({ breakpoints: fbpts }).then(response => {
|
||||
if (response && response.body) {
|
||||
const data = new Map<string, DebugProtocol.Breakpoint>();
|
||||
for (let i = 0; i < fbpts.length; i++) {
|
||||
data.set(fbpts[i].getId(), response.body.breakpoints[i]);
|
||||
}
|
||||
this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
async sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
await this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) });
|
||||
}
|
||||
}
|
||||
|
||||
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void> {
|
||||
if (this.raw) {
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
return this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) }).then(() => undefined);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
async dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
if (!this.raw.readyForBreakpoints) {
|
||||
throw new Error(nls.localize('sessionNotReadyForBreakpoints', "Session is not ready for breakpoints"));
|
||||
}
|
||||
|
||||
const response = await this.raw.dataBreakpointInfo({ name, variablesReference });
|
||||
return response.body;
|
||||
}
|
||||
|
||||
dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }> {
|
||||
if (this.raw) {
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
return this.raw.dataBreakpointInfo({ name, variablesReference }).then(response => response.body);
|
||||
}
|
||||
return Promise.reject(new Error(nls.localize('sessionNotReadyForBreakpoints', "Session is not ready for breakpoints")));
|
||||
async sendDataBreakpoints(dataBreakpoints: IDataBreakpoint[]): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
sendDataBreakpoints(dataBreakpoints: IDataBreakpoint[]): Promise<void> {
|
||||
if (this.raw) {
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
return this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints }).then(response => {
|
||||
if (response && response.body) {
|
||||
const data = new Map<string, DebugProtocol.Breakpoint>();
|
||||
for (let i = 0; i < dataBreakpoints.length; i++) {
|
||||
data.set(dataBreakpoints[i].getId(), response.body.breakpoints[i]);
|
||||
}
|
||||
this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
|
||||
}
|
||||
});
|
||||
if (this.raw.readyForBreakpoints) {
|
||||
const response = await this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints });
|
||||
if (response && response.body) {
|
||||
const data = new Map<string, DebugProtocol.Breakpoint>();
|
||||
for (let i = 0; i < dataBreakpoints.length; i++) {
|
||||
data.set(dataBreakpoints[i].getId(), response.body.breakpoints[i]);
|
||||
}
|
||||
this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
async breakpointsLocations(uri: URI, lineNumber: number): Promise<IPosition[]> {
|
||||
if (this.raw) {
|
||||
const source = this.getRawSource(uri);
|
||||
const response = await this.raw.breakpointLocations({ source, line: lineNumber });
|
||||
const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 }));
|
||||
|
||||
return distinct(positions, p => `${p.lineNumber}:${p.column}`);
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
const source = this.getRawSource(uri);
|
||||
const response = await this.raw.breakpointLocations({ source, line: lineNumber });
|
||||
if (!response.body || !response.body.breakpoints) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 }));
|
||||
|
||||
return distinct(positions, p => `${p.lineNumber}:${p.column}`);
|
||||
}
|
||||
|
||||
customRequest(request: string, args: any): Promise<DebugProtocol.Response> {
|
||||
if (this.raw) {
|
||||
return this.raw.custom(request, args);
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
return this.raw.custom(request, args);
|
||||
}
|
||||
|
||||
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse> {
|
||||
if (this.raw) {
|
||||
const token = this.getNewCancellationToken(threadId);
|
||||
return this.raw.stackTrace({ threadId, startFrame, levels }, token);
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
const token = this.getNewCancellationToken(threadId);
|
||||
return this.raw.stackTrace({ threadId, startFrame, levels }, token);
|
||||
}
|
||||
|
||||
exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined> {
|
||||
if (this.raw) {
|
||||
return this.raw.exceptionInfo({ threadId }).then(response => {
|
||||
if (response) {
|
||||
return {
|
||||
id: response.body.exceptionId,
|
||||
description: response.body.description,
|
||||
breakMode: response.body.breakMode,
|
||||
details: response.body.details
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
async exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
const response = await this.raw.exceptionInfo({ threadId });
|
||||
if (response) {
|
||||
return {
|
||||
id: response.body.exceptionId,
|
||||
description: response.body.description,
|
||||
breakMode: response.body.breakMode,
|
||||
details: response.body.details
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
scopes(frameId: number, threadId: number): Promise<DebugProtocol.ScopesResponse> {
|
||||
if (this.raw) {
|
||||
const token = this.getNewCancellationToken(threadId);
|
||||
return this.raw.scopes({ frameId }, token);
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
const token = this.getNewCancellationToken(threadId);
|
||||
return this.raw.scopes({ frameId }, token);
|
||||
}
|
||||
|
||||
variables(variablesReference: number, threadId: number | undefined, filter: 'indexed' | 'named' | undefined, start: number | undefined, count: number | undefined): Promise<DebugProtocol.VariablesResponse> {
|
||||
if (this.raw) {
|
||||
const token = threadId ? this.getNewCancellationToken(threadId) : undefined;
|
||||
return this.raw.variables({ variablesReference, filter, start, count }, token);
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
const token = threadId ? this.getNewCancellationToken(threadId) : undefined;
|
||||
return this.raw.variables({ variablesReference, filter, start, count }, token);
|
||||
}
|
||||
|
||||
evaluate(expression: string, frameId: number, context?: string): Promise<DebugProtocol.EvaluateResponse> {
|
||||
if (this.raw) {
|
||||
return this.raw.evaluate({ expression, frameId, context });
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
return this.raw.evaluate({ expression, frameId, context });
|
||||
}
|
||||
|
||||
restartFrame(frameId: number, threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.restartFrame({ frameId }, threadId).then(() => undefined);
|
||||
async restartFrame(frameId: number, threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.restartFrame({ frameId }, threadId);
|
||||
}
|
||||
|
||||
next(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.next({ threadId }).then(() => undefined);
|
||||
async next(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.next({ threadId });
|
||||
}
|
||||
|
||||
stepIn(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.stepIn({ threadId }).then(() => undefined);
|
||||
async stepIn(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.stepIn({ threadId });
|
||||
}
|
||||
|
||||
stepOut(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.stepOut({ threadId }).then(() => undefined);
|
||||
async stepOut(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.stepOut({ threadId });
|
||||
}
|
||||
|
||||
stepBack(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.stepBack({ threadId }).then(() => undefined);
|
||||
async stepBack(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.stepBack({ threadId });
|
||||
}
|
||||
|
||||
continue(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.continue({ threadId }).then(() => undefined);
|
||||
async continue(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.continue({ threadId });
|
||||
}
|
||||
|
||||
reverseContinue(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.reverseContinue({ threadId }).then(() => undefined);
|
||||
async reverseContinue(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.reverseContinue({ threadId });
|
||||
}
|
||||
|
||||
pause(threadId: number): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.pause({ threadId }).then(() => undefined);
|
||||
async pause(threadId: number): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.pause({ threadId });
|
||||
}
|
||||
|
||||
terminateThreads(threadIds?: number[]): Promise<void> {
|
||||
if (this.raw) {
|
||||
return this.raw.terminateThreads({ threadIds }).then(() => undefined);
|
||||
async terminateThreads(threadIds?: number[]): Promise<void> {
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
await this.raw.terminateThreads({ threadIds });
|
||||
}
|
||||
|
||||
setVariable(variablesReference: number, name: string, value: string): Promise<DebugProtocol.SetVariableResponse> {
|
||||
if (this.raw) {
|
||||
return this.raw.setVariable({ variablesReference, name, value });
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
return this.raw.setVariable({ variablesReference, name, value });
|
||||
}
|
||||
|
||||
gotoTargets(source: DebugProtocol.Source, line: number, column?: number): Promise<DebugProtocol.GotoTargetsResponse> {
|
||||
if (this.raw) {
|
||||
return this.raw.gotoTargets({ source, line, column });
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
return this.raw.gotoTargets({ source, line, column });
|
||||
}
|
||||
|
||||
goto(threadId: number, targetId: number): Promise<DebugProtocol.GotoResponse> {
|
||||
if (this.raw) {
|
||||
return this.raw.goto({ threadId, targetId });
|
||||
if (!this.raw) {
|
||||
throw new Error('no debug adapter');
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
return this.raw.goto({ threadId, targetId });
|
||||
}
|
||||
|
||||
loadSource(resource: URI): Promise<DebugProtocol.SourceResponse> {
|
||||
|
||||
if (!this.raw) {
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
@@ -546,50 +552,48 @@ export class DebugSession implements IDebugSession {
|
||||
return this.raw.source({ sourceReference: rawSource.sourceReference || 0, source: rawSource });
|
||||
}
|
||||
|
||||
getLoadedSources(): Promise<Source[]> {
|
||||
if (this.raw) {
|
||||
return this.raw.loadedSources({}).then(response => {
|
||||
if (response.body && response.body.sources) {
|
||||
return response.body.sources.map(src => this.getSource(src));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}, () => {
|
||||
return [];
|
||||
});
|
||||
async getLoadedSources(): Promise<Source[]> {
|
||||
if (!this.raw) {
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
const response = await this.raw.loadedSources({});
|
||||
if (response.body && response.body.sources) {
|
||||
return response.body.sources.map(src => this.getSource(src));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<CompletionItem[]> {
|
||||
if (this.raw) {
|
||||
return this.raw.completions({
|
||||
frameId,
|
||||
text,
|
||||
column: position.column,
|
||||
line: position.lineNumber,
|
||||
}, token).then(response => {
|
||||
async completions(frameId: number | undefined, text: string, position: Position, overwriteBefore: number, token: CancellationToken): Promise<CompletionItem[]> {
|
||||
if (!this.raw) {
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
const result: CompletionItem[] = [];
|
||||
if (response && response.body && response.body.targets) {
|
||||
response.body.targets.forEach(item => {
|
||||
if (item && item.label) {
|
||||
result.push({
|
||||
label: item.label,
|
||||
insertText: item.text || item.label,
|
||||
kind: completionKindFromString(item.type || 'property'),
|
||||
filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined,
|
||||
range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position),
|
||||
sortText: item.sortText
|
||||
});
|
||||
}
|
||||
const response = await this.raw.completions({
|
||||
frameId,
|
||||
text,
|
||||
column: position.column,
|
||||
line: position.lineNumber,
|
||||
}, token);
|
||||
|
||||
const result: CompletionItem[] = [];
|
||||
if (response && response.body && response.body.targets) {
|
||||
response.body.targets.forEach(item => {
|
||||
if (item && item.label) {
|
||||
result.push({
|
||||
label: item.label,
|
||||
insertText: item.text || item.label,
|
||||
kind: completionKindFromString(item.type || 'property'),
|
||||
filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined,
|
||||
range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position),
|
||||
sortText: item.sortText
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//---- threads
|
||||
@@ -674,8 +678,9 @@ export class DebugSession implements IDebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
private fetchThreads(stoppedDetails?: IRawStoppedDetails): Promise<void> {
|
||||
return this.raw ? this.raw.threads().then(response => {
|
||||
private async fetchThreads(stoppedDetails?: IRawStoppedDetails): Promise<void> {
|
||||
if (this.raw) {
|
||||
const response = await this.raw.threads();
|
||||
if (response && response.body && response.body.threads) {
|
||||
this.model.rawUpdate({
|
||||
sessionId: this.getId(),
|
||||
@@ -683,7 +688,7 @@ export class DebugSession implements IDebugSession {
|
||||
stoppedDetails
|
||||
});
|
||||
}
|
||||
}) : Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
//---- private
|
||||
@@ -693,60 +698,63 @@ export class DebugSession implements IDebugSession {
|
||||
return;
|
||||
}
|
||||
|
||||
this.rawListeners.push(this.raw.onDidInitialize(() => {
|
||||
this.rawListeners.push(this.raw.onDidInitialize(async () => {
|
||||
aria.status(nls.localize('debuggingStarted', "Debugging started."));
|
||||
const sendConfigurationDone = () => {
|
||||
const sendConfigurationDone = async () => {
|
||||
if (this.raw && this.raw.capabilities.supportsConfigurationDoneRequest) {
|
||||
return this.raw.configurationDone().then(undefined, e => {
|
||||
try {
|
||||
await this.raw.configurationDone();
|
||||
} catch (e) {
|
||||
// Disconnect the debug session on configuration done error #10596
|
||||
if (this.raw) {
|
||||
this.raw.disconnect();
|
||||
}
|
||||
if (e.command !== 'canceled' && e.message !== 'canceled') {
|
||||
this.notificationService.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Send all breakpoints
|
||||
this.debugService.sendAllBreakpoints(this).then(sendConfigurationDone, sendConfigurationDone)
|
||||
.then(() => this.fetchThreads());
|
||||
try {
|
||||
await this.debugService.sendAllBreakpoints(this);
|
||||
} finally {
|
||||
await sendConfigurationDone();
|
||||
}
|
||||
await this.fetchThreads();
|
||||
}));
|
||||
|
||||
this.rawListeners.push(this.raw.onDidStop(event => {
|
||||
this.fetchThreads(event.body).then(() => {
|
||||
const thread = typeof event.body.threadId === 'number' ? this.getThread(event.body.threadId) : undefined;
|
||||
if (thread) {
|
||||
// Call fetch call stack twice, the first only return the top stack frame.
|
||||
// Second retrieves the rest of the call stack. For performance reasons #25605
|
||||
const promises = this.model.fetchCallStack(<Thread>thread);
|
||||
const focus = () => {
|
||||
if (!event.body.preserveFocusHint && thread.getCallStack().length) {
|
||||
this.debugService.focusStackFrame(undefined, thread);
|
||||
if (thread.stoppedDetails) {
|
||||
if (this.configurationService.getValue<IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') {
|
||||
this.viewletService.openViewlet(VIEWLET_ID);
|
||||
}
|
||||
this.rawListeners.push(this.raw.onDidStop(async event => {
|
||||
await this.fetchThreads(event.body);
|
||||
const thread = typeof event.body.threadId === 'number' ? this.getThread(event.body.threadId) : undefined;
|
||||
if (thread) {
|
||||
// Call fetch call stack twice, the first only return the top stack frame.
|
||||
// Second retrieves the rest of the call stack. For performance reasons #25605
|
||||
const promises = this.model.fetchCallStack(<Thread>thread);
|
||||
const focus = async () => {
|
||||
if (!event.body.preserveFocusHint && thread.getCallStack().length) {
|
||||
await this.debugService.focusStackFrame(undefined, thread);
|
||||
if (thread.stoppedDetails) {
|
||||
if (this.configurationService.getValue<IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') {
|
||||
this.viewletService.openViewlet(VIEWLET_ID);
|
||||
}
|
||||
|
||||
if (this.configurationService.getValue<IDebugConfiguration>('debug').focusWindowOnBreak) {
|
||||
this.hostService.focus();
|
||||
}
|
||||
if (this.configurationService.getValue<IDebugConfiguration>('debug').focusWindowOnBreak) {
|
||||
this.hostService.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
promises.topCallStack.then(focus);
|
||||
promises.wholeCallStack.then(() => {
|
||||
if (!this.debugService.getViewModel().focusedStackFrame) {
|
||||
// The top stack frame can be deemphesized so try to focus again #68616
|
||||
focus();
|
||||
}
|
||||
});
|
||||
await promises.topCallStack;
|
||||
focus();
|
||||
await promises.wholeCallStack;
|
||||
if (!this.debugService.getViewModel().focusedStackFrame) {
|
||||
// The top stack frame can be deemphesized so try to focus again #68616
|
||||
focus();
|
||||
}
|
||||
}).then(() => this._onDidChangeState.fire());
|
||||
}
|
||||
this._onDidChangeState.fire();
|
||||
}));
|
||||
|
||||
this.rawListeners.push(this.raw.onDidThread(event => {
|
||||
@@ -763,15 +771,21 @@ export class DebugSession implements IDebugSession {
|
||||
}
|
||||
} else if (event.body.reason === 'exited') {
|
||||
this.model.clearThreads(this.getId(), true, event.body.threadId);
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
const focusedThread = viewModel.focusedThread;
|
||||
if (focusedThread && event.body.threadId === focusedThread.threadId) {
|
||||
// De-focus the thread in case it was focused
|
||||
this.debugService.focusStackFrame(undefined, undefined, viewModel.focusedSession, false);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.rawListeners.push(this.raw.onDidTerminateDebugee(event => {
|
||||
this.rawListeners.push(this.raw.onDidTerminateDebugee(async event => {
|
||||
aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
|
||||
if (event.body && event.body.restart) {
|
||||
this.debugService.restartSession(this, event.body.restart).then(undefined, onUnexpectedError);
|
||||
await this.debugService.restartSession(this, event.body.restart);
|
||||
} else if (this.raw) {
|
||||
this.raw.disconnect();
|
||||
await this.raw.disconnect();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -792,7 +806,7 @@ export class DebugSession implements IDebugSession {
|
||||
}));
|
||||
|
||||
let outpuPromises: Promise<void>[] = [];
|
||||
this.rawListeners.push(this.raw.onDidOutput(event => {
|
||||
this.rawListeners.push(this.raw.onDidOutput(async event => {
|
||||
if (!event.body || !this.raw) {
|
||||
return;
|
||||
}
|
||||
@@ -818,17 +832,21 @@ export class DebugSession implements IDebugSession {
|
||||
} : undefined;
|
||||
if (event.body.variablesReference) {
|
||||
const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
|
||||
outpuPromises.push(container.getChildren().then(children => {
|
||||
return Promise.all(waitFor).then(() => children.forEach(child => {
|
||||
outpuPromises.push(container.getChildren().then(async children => {
|
||||
await Promise.all(waitFor);
|
||||
children.forEach(child => {
|
||||
// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
|
||||
(<any>child).name = null;
|
||||
this.appendToRepl(child, outputSeverity, source);
|
||||
}));
|
||||
});
|
||||
}));
|
||||
} else if (typeof event.body.output === 'string') {
|
||||
Promise.all(waitFor).then(() => this.appendToRepl(event.body.output, outputSeverity, source));
|
||||
await Promise.all(waitFor);
|
||||
this.appendToRepl(event.body.output, outputSeverity, source);
|
||||
}
|
||||
Promise.all(outpuPromises).then(() => outpuPromises = []);
|
||||
|
||||
await Promise.all(outpuPromises);
|
||||
outpuPromises = [];
|
||||
}));
|
||||
|
||||
this.rawListeners.push(this.raw.onDidBreakpoint(event => {
|
||||
|
||||
@@ -176,7 +176,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
|
||||
});
|
||||
}));
|
||||
|
||||
this._register(this.layoutService.onTitleBarVisibilityChange(() => this.setYCoordinate()));
|
||||
this._register(this.layoutService.onPartVisibilityChange(() => this.setYCoordinate()));
|
||||
this._register(browser.onDidChangeZoomLevel(() => this.setYCoordinate()));
|
||||
}
|
||||
|
||||
@@ -192,10 +192,10 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
|
||||
super.updateStyles();
|
||||
|
||||
if (this.$el) {
|
||||
this.$el.style.backgroundColor = this.getColor(debugToolBarBackground);
|
||||
this.$el.style.backgroundColor = this.getColor(debugToolBarBackground) || '';
|
||||
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null;
|
||||
this.$el.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : '';
|
||||
|
||||
const contrastBorderColor = this.getColor(contrastBorder);
|
||||
const borderColor = this.getColor(debugToolBarBorder);
|
||||
|
||||
@@ -214,7 +214,7 @@ export class DebugViewlet extends ViewContainerViewlet {
|
||||
|
||||
class ToggleReplAction extends TogglePanelAction {
|
||||
static readonly ID = 'debug.toggleRepl';
|
||||
static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console');
|
||||
static readonly LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugConsoleAction' }, 'Debug Console');
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug';
|
||||
@@ -13,17 +12,20 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mapToSerializable } from 'vs/base/common/map';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
|
||||
|
||||
class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService {
|
||||
|
||||
private workspaceProvider: IWorkspaceProvider;
|
||||
|
||||
constructor(
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
const connection = remoteAgentService.getConnection();
|
||||
|
||||
let channel: IChannel;
|
||||
if (connection) {
|
||||
channel = connection.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName);
|
||||
@@ -35,11 +37,26 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
||||
|
||||
super(channel);
|
||||
|
||||
if (environmentService.options && environmentService.options.workspaceProvider) {
|
||||
this.workspaceProvider = environmentService.options.workspaceProvider;
|
||||
} else {
|
||||
this.workspaceProvider = { open: async () => undefined, workspace: undefined };
|
||||
console.warn('Extension Host Debugging not available due to missing workspace provider.');
|
||||
}
|
||||
|
||||
this.registerListeners(environmentService);
|
||||
}
|
||||
|
||||
private registerListeners(environmentService: IWorkbenchEnvironmentService): void {
|
||||
|
||||
// Reload window on reload request
|
||||
this._register(this.onReload(event => {
|
||||
if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) {
|
||||
window.location.reload();
|
||||
}
|
||||
}));
|
||||
|
||||
// Close window on close request
|
||||
this._register(this.onClose(event => {
|
||||
if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) {
|
||||
window.close();
|
||||
@@ -47,8 +64,46 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
||||
}));
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
// we pass the "ParsedArgs" as query parameters of the URL
|
||||
async openExtensionDevelopmentHostWindow(args: string[]): Promise<void> {
|
||||
if (!this.workspaceProvider.payload) {
|
||||
// TODO@Ben remove me once environment is adopted
|
||||
return this.openExtensionDevelopmentHostWindowLegacy(args);
|
||||
}
|
||||
|
||||
// Find out which workspace to open debug window on
|
||||
let debugWorkspace: IWorkspace = undefined;
|
||||
const folderUriArg = this.findArgument('folder-uri', args);
|
||||
if (folderUriArg) {
|
||||
debugWorkspace = { folderUri: URI.parse(folderUriArg) };
|
||||
}
|
||||
|
||||
// Add environment parameters required for debug to work
|
||||
const environment = new Map<string, string>();
|
||||
|
||||
const extensionDevelopmentPath = this.findArgument('extensionDevelopmentPath', args);
|
||||
if (extensionDevelopmentPath) {
|
||||
environment.set('extensionDevelopmentPath', extensionDevelopmentPath);
|
||||
}
|
||||
|
||||
const debugId = this.findArgument('debugId', args);
|
||||
if (debugId) {
|
||||
environment.set('debugId', debugId);
|
||||
}
|
||||
|
||||
const inspectBrkExtensions = this.findArgument('inspect-brk-extensions', args);
|
||||
if (inspectBrkExtensions) {
|
||||
environment.set('inspect-brk-extensions', inspectBrkExtensions);
|
||||
}
|
||||
|
||||
// Open debug window as new window. Pass ParsedArgs over.
|
||||
this.workspaceProvider.open(debugWorkspace, {
|
||||
reuse: false, // debugging always requires a new window
|
||||
payload: mapToSerializable(environment) // mandatory properties to enable debugging
|
||||
});
|
||||
}
|
||||
|
||||
private async openExtensionDevelopmentHostWindowLegacy(args: string[]): Promise<void> {
|
||||
// we pass the "args" as query parameters of the URL
|
||||
|
||||
let newAddress = `${document.location.origin}${document.location.pathname}?`;
|
||||
let gotFolder = false;
|
||||
@@ -61,9 +116,19 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
||||
newAddress += `${key}=${encodeURIComponent(value)}`;
|
||||
};
|
||||
|
||||
const f = args['folder-uri'];
|
||||
const findArgument = (key: string) => {
|
||||
for (let a of args) {
|
||||
const k = `--${key}=`;
|
||||
if (a.indexOf(k) === 0) {
|
||||
return a.substr(k.length);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const f = findArgument('folder-uri');
|
||||
if (f) {
|
||||
const u = URI.parse(f[0]);
|
||||
const u = URI.parse(f);
|
||||
gotFolder = true;
|
||||
addQueryParameter('folder', u.path);
|
||||
}
|
||||
@@ -72,26 +137,36 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i
|
||||
addQueryParameter('ew', 'true');
|
||||
}
|
||||
|
||||
const ep = args['extensionDevelopmentPath'];
|
||||
const ep = findArgument('extensionDevelopmentPath');
|
||||
if (ep) {
|
||||
let u = ep[0];
|
||||
addQueryParameter('edp', u);
|
||||
addQueryParameter('extensionDevelopmentPath', ep);
|
||||
}
|
||||
|
||||
const di = args['debugId'];
|
||||
const di = findArgument('debugId');
|
||||
if (di) {
|
||||
addQueryParameter('di', di);
|
||||
addQueryParameter('debugId', di);
|
||||
}
|
||||
|
||||
const ibe = args['inspect-brk-extensions'];
|
||||
const ibe = findArgument('inspect-brk-extensions');
|
||||
if (ibe) {
|
||||
addQueryParameter('ibe', ibe);
|
||||
addQueryParameter('inspect-brk-extensions', ibe);
|
||||
}
|
||||
|
||||
window.open(newAddress);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private findArgument(key: string, args: string[]): string | undefined {
|
||||
for (const a of args) {
|
||||
const k = `--${key}=`;
|
||||
if (a.indexOf(k) === 0) {
|
||||
return a.substr(k.length);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService);
|
||||
|
||||
@@ -240,7 +240,7 @@ class RootTreeItem extends BaseTreeItem {
|
||||
|
||||
class SessionTreeItem extends BaseTreeItem {
|
||||
|
||||
private static URL_REGEXP = /^(https?:\/\/[^/]+)(\/.*)$/;
|
||||
private static readonly URL_REGEXP = /^(https?:\/\/[^/]+)(\/.*)$/;
|
||||
|
||||
private _session: IDebugSession;
|
||||
private _initialized: boolean;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
.debug-breakpoint-hint {
|
||||
background: url('breakpoint-hint.svg') center center no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.debug-breakpoint-disabled,
|
||||
@@ -50,7 +51,7 @@
|
||||
.monaco-editor .debug-breakpoint-placeholder::before,
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
content: " ";
|
||||
width: 1.3em;
|
||||
width: 0.9em;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 2px;
|
||||
@@ -62,9 +63,6 @@
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
margin-left: 0.61em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -178,7 +176,7 @@
|
||||
/* White color when element is selected and list is focused. White looks better on blue selection background. */
|
||||
.monaco-workbench .monaco-list:focus .monaco-list-row.selected .expression .name,
|
||||
.monaco-workbench .monaco-list:focus .monaco-list-row.selected .expression .value {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-list-row .expression .name {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
padding-left: 15px;
|
||||
padding-right: 2px;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
word-break: normal;
|
||||
text-overflow: ellipsis;
|
||||
height: 18px;
|
||||
|
||||
@@ -12,7 +12,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { formatPII, isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
@@ -228,25 +227,23 @@ export class RawDebugSession implements IDisposable {
|
||||
/**
|
||||
* Starts the underlying debug adapter and tracks the session time for telemetry.
|
||||
*/
|
||||
start(): Promise<void> {
|
||||
async start(): Promise<void> {
|
||||
if (!this.debugAdapter) {
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
return this.debugAdapter.startSession().then(() => {
|
||||
this.startTime = new Date().getTime();
|
||||
}, err => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
|
||||
await this.debugAdapter.startSession();
|
||||
this.startTime = new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send client capabilities to the debug adapter and receive DA capabilities in return.
|
||||
*/
|
||||
initialize(args: DebugProtocol.InitializeRequestArguments): Promise<DebugProtocol.InitializeResponse> {
|
||||
return this.send('initialize', args).then((response: DebugProtocol.InitializeResponse) => {
|
||||
this.mergeCapabilities(response.body);
|
||||
return response;
|
||||
});
|
||||
async initialize(args: DebugProtocol.InitializeRequestArguments): Promise<DebugProtocol.InitializeResponse> {
|
||||
const response = await this.send('initialize', args);
|
||||
this.mergeCapabilities(response.body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,11 +255,11 @@ export class RawDebugSession implements IDisposable {
|
||||
|
||||
//---- DAP requests
|
||||
|
||||
launchOrAttach(config: IConfig): Promise<DebugProtocol.Response> {
|
||||
return this.send(config.request, config).then(response => {
|
||||
this.mergeCapabilities(response.body);
|
||||
return response;
|
||||
});
|
||||
async launchOrAttach(config: IConfig): Promise<DebugProtocol.Response> {
|
||||
const response = await this.send(config.request, config);
|
||||
this.mergeCapabilities(response.body);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,35 +283,32 @@ export class RawDebugSession implements IDisposable {
|
||||
return Promise.reject(new Error('restart not supported'));
|
||||
}
|
||||
|
||||
next(args: DebugProtocol.NextArguments): Promise<DebugProtocol.NextResponse> {
|
||||
return this.send('next', args).then(response => {
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
});
|
||||
async next(args: DebugProtocol.NextArguments): Promise<DebugProtocol.NextResponse> {
|
||||
const response = await this.send('next', args);
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
}
|
||||
|
||||
stepIn(args: DebugProtocol.StepInArguments): Promise<DebugProtocol.StepInResponse> {
|
||||
return this.send('stepIn', args).then(response => {
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
});
|
||||
async stepIn(args: DebugProtocol.StepInArguments): Promise<DebugProtocol.StepInResponse> {
|
||||
const response = await this.send('stepIn', args);
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
}
|
||||
|
||||
stepOut(args: DebugProtocol.StepOutArguments): Promise<DebugProtocol.StepOutResponse> {
|
||||
return this.send('stepOut', args).then(response => {
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
});
|
||||
async stepOut(args: DebugProtocol.StepOutArguments): Promise<DebugProtocol.StepOutResponse> {
|
||||
const response = await this.send('stepOut', args);
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
}
|
||||
|
||||
continue(args: DebugProtocol.ContinueArguments): Promise<DebugProtocol.ContinueResponse> {
|
||||
return this.send<DebugProtocol.ContinueResponse>('continue', args).then(response => {
|
||||
if (response && response.body && response.body.allThreadsContinued !== undefined) {
|
||||
this.allThreadsContinued = response.body.allThreadsContinued;
|
||||
}
|
||||
this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued);
|
||||
return response;
|
||||
});
|
||||
async continue(args: DebugProtocol.ContinueArguments): Promise<DebugProtocol.ContinueResponse> {
|
||||
const response = await this.send<DebugProtocol.ContinueResponse>('continue', args);
|
||||
if (response && response.body && response.body.allThreadsContinued !== undefined) {
|
||||
this.allThreadsContinued = response.body.allThreadsContinued;
|
||||
}
|
||||
this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
pause(args: DebugProtocol.PauseArguments): Promise<DebugProtocol.PauseResponse> {
|
||||
@@ -335,12 +329,11 @@ export class RawDebugSession implements IDisposable {
|
||||
return Promise.reject(new Error('setVariable not supported'));
|
||||
}
|
||||
|
||||
restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise<DebugProtocol.RestartFrameResponse> {
|
||||
async restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise<DebugProtocol.RestartFrameResponse> {
|
||||
if (this.capabilities.supportsRestartFrame) {
|
||||
return this.send('restartFrame', args).then(response => {
|
||||
this.fireSimulatedContinuedEvent(threadId);
|
||||
return response;
|
||||
});
|
||||
const response = await this.send('restartFrame', args);
|
||||
this.fireSimulatedContinuedEvent(threadId);
|
||||
return response;
|
||||
}
|
||||
return Promise.reject(new Error('restartFrame not supported'));
|
||||
}
|
||||
@@ -433,26 +426,24 @@ export class RawDebugSession implements IDisposable {
|
||||
return this.send<DebugProtocol.EvaluateResponse>('evaluate', args);
|
||||
}
|
||||
|
||||
stepBack(args: DebugProtocol.StepBackArguments): Promise<DebugProtocol.StepBackResponse> {
|
||||
async stepBack(args: DebugProtocol.StepBackArguments): Promise<DebugProtocol.StepBackResponse> {
|
||||
if (this.capabilities.supportsStepBack) {
|
||||
return this.send('stepBack', args).then(response => {
|
||||
if (response.body === undefined) { // TODO@AW why this check?
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
const response = await this.send('stepBack', args);
|
||||
if (response.body === undefined) { // TODO@AW why this check?
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
return Promise.reject(new Error('stepBack not supported'));
|
||||
}
|
||||
|
||||
reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise<DebugProtocol.ReverseContinueResponse> {
|
||||
async reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise<DebugProtocol.ReverseContinueResponse> {
|
||||
if (this.capabilities.supportsStepBack) {
|
||||
return this.send('reverseContinue', args).then(response => {
|
||||
if (response.body === undefined) { // TODO@AW why this check?
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
const response = await this.send('reverseContinue', args);
|
||||
if (response.body === undefined) { // TODO@AW why this check?
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
return Promise.reject(new Error('reverseContinue not supported'));
|
||||
}
|
||||
@@ -464,13 +455,13 @@ export class RawDebugSession implements IDisposable {
|
||||
return Promise.reject(new Error('gotoTargets is not supported'));
|
||||
}
|
||||
|
||||
goto(args: DebugProtocol.GotoArguments): Promise<DebugProtocol.GotoResponse> {
|
||||
async goto(args: DebugProtocol.GotoArguments): Promise<DebugProtocol.GotoResponse> {
|
||||
if (this.capabilities.supportsGotoTargetsRequest) {
|
||||
return this.send('goto', args).then(res => {
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return res;
|
||||
});
|
||||
const response = await this.send('goto', args);
|
||||
this.fireSimulatedContinuedEvent(args.threadId);
|
||||
return response;
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('goto is not supported'));
|
||||
}
|
||||
|
||||
@@ -484,36 +475,32 @@ export class RawDebugSession implements IDisposable {
|
||||
|
||||
//---- private
|
||||
|
||||
private shutdown(error?: Error, restart = false): Promise<any> {
|
||||
private async shutdown(error?: Error, restart = false): Promise<any> {
|
||||
if (!this.inShutdown) {
|
||||
this.inShutdown = true;
|
||||
if (this.debugAdapter) {
|
||||
return this.send('disconnect', { restart }, undefined, 500).then(() => {
|
||||
try {
|
||||
await this.send('disconnect', { restart }, undefined, 500);
|
||||
} finally {
|
||||
this.stopAdapter(error);
|
||||
}, () => {
|
||||
// ignore error
|
||||
this.stopAdapter(error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return this.stopAdapter(error);
|
||||
}
|
||||
return this.stopAdapter(error);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private stopAdapter(error?: Error): Promise<any> {
|
||||
if (this.debugAdapter) {
|
||||
const da = this.debugAdapter;
|
||||
this.debugAdapter = null;
|
||||
return da.stopSession().then(_ => {
|
||||
private async stopAdapter(error?: Error): Promise<any> {
|
||||
try {
|
||||
if (this.debugAdapter) {
|
||||
const da = this.debugAdapter;
|
||||
this.debugAdapter = null;
|
||||
await da.stopSession();
|
||||
this.debugAdapterStopped = true;
|
||||
this.fireAdapterExitEvent(error);
|
||||
}, err => {
|
||||
this.fireAdapterExitEvent(error);
|
||||
});
|
||||
} else {
|
||||
}
|
||||
} finally {
|
||||
this.fireAdapterExitEvent(error);
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private fireAdapterExitEvent(error?: Error): void {
|
||||
@@ -557,18 +544,19 @@ export class RawDebugSession implements IDisposable {
|
||||
});
|
||||
break;
|
||||
case 'runInTerminal':
|
||||
dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments).then(shellProcessId => {
|
||||
try {
|
||||
const shellProcessId = await dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments);
|
||||
const resp = response as DebugProtocol.RunInTerminalResponse;
|
||||
resp.body = {};
|
||||
if (typeof shellProcessId === 'number') {
|
||||
resp.body.shellProcessId = shellProcessId;
|
||||
}
|
||||
safeSendResponse(resp);
|
||||
}, err => {
|
||||
} catch (err) {
|
||||
response.success = false;
|
||||
response.message = err.message;
|
||||
safeSendResponse(response);
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
response.success = false;
|
||||
@@ -580,9 +568,7 @@ export class RawDebugSession implements IDisposable {
|
||||
|
||||
private launchVsCode(vscodeArgs: ILaunchVSCodeArguments): Promise<void> {
|
||||
|
||||
let args: ParsedArgs = {
|
||||
_: []
|
||||
};
|
||||
const args: string[] = [];
|
||||
|
||||
for (let arg of vscodeArgs.args) {
|
||||
if (arg.prefix) {
|
||||
@@ -594,32 +580,11 @@ export class RawDebugSession implements IDisposable {
|
||||
|
||||
if ((key === 'file-uri' || key === 'folder-uri') && !isUri(arg.path)) {
|
||||
value = URI.file(value).toString();
|
||||
|
||||
const v = args[key];
|
||||
if (v) {
|
||||
v.push(value);
|
||||
} else {
|
||||
args[key] = [value];
|
||||
}
|
||||
} else if (key === 'extensionDevelopmentPath' || key === 'enable-proposed-api') {
|
||||
const v = args[key];
|
||||
if (v) {
|
||||
v.push(value);
|
||||
} else {
|
||||
args[key] = [value];
|
||||
}
|
||||
} else {
|
||||
(<any>args)[key] = value;
|
||||
}
|
||||
|
||||
args.push(`--${key}=${value}`);
|
||||
} else {
|
||||
const match = /^--(.+)$/.exec(a2);
|
||||
if (match && match.length === 2) {
|
||||
const key = match[1];
|
||||
(<any>args)[key] = true;
|
||||
} else {
|
||||
args._.push(a2);
|
||||
}
|
||||
args.push(a2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { Variable } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { renderExpressionValue, AbstractExpressionsRenderer, IExpressionTemplateData, renderVariable, IInputBoxOptions } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
@@ -74,8 +74,8 @@ interface IPrivateReplService {
|
||||
_serviceBrand: undefined;
|
||||
acceptReplInput(): void;
|
||||
getVisibleContent(): string;
|
||||
selectSession(session?: IDebugSession): void;
|
||||
clearRepl(): void;
|
||||
selectSession(session?: IDebugSession): Promise<void>;
|
||||
clearRepl(): Promise<void>;
|
||||
focusRepl(): void;
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.debugService.getViewModel().onDidFocusSession(session => {
|
||||
this._register(this.debugService.getViewModel().onDidFocusSession(async session => {
|
||||
if (session) {
|
||||
sessionsToIgnore.delete(session);
|
||||
if (this.completionItemProvider) {
|
||||
@@ -159,13 +159,13 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
}
|
||||
}
|
||||
|
||||
this.selectSession();
|
||||
await this.selectSession();
|
||||
}));
|
||||
this._register(this.debugService.onWillNewSession(newSession => {
|
||||
this._register(this.debugService.onWillNewSession(async newSession => {
|
||||
// Need to listen to output events for sessions which are not yet fully initialised
|
||||
const input = this.tree.getInput();
|
||||
if (!input || input.state === State.Inactive) {
|
||||
this.selectSession(newSession);
|
||||
await this.selectSession(newSession);
|
||||
}
|
||||
this.updateTitleArea();
|
||||
}));
|
||||
@@ -252,7 +252,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
}
|
||||
}
|
||||
|
||||
selectSession(session?: IDebugSession): void {
|
||||
async selectSession(session?: IDebugSession): Promise<void> {
|
||||
const treeInput = this.tree.getInput();
|
||||
if (!session) {
|
||||
const focusedSession = this.debugService.getViewModel().focusedSession;
|
||||
@@ -272,7 +272,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
});
|
||||
|
||||
if (this.tree && treeInput !== session) {
|
||||
this.tree.setInput(session).then(() => revealLastElement(this.tree)).then(undefined, errors.onUnexpectedError);
|
||||
await this.tree.setInput(session);
|
||||
revealLastElement(this.tree);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,14 +281,14 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
|
||||
this.updateInputDecoration();
|
||||
}
|
||||
|
||||
clearRepl(): void {
|
||||
async clearRepl(): Promise<void> {
|
||||
const session = this.tree.getInput();
|
||||
if (session) {
|
||||
session.removeReplExpressions();
|
||||
if (session.state === State.Inactive) {
|
||||
// Ignore inactive sessions which got cleared - so they are not shown any more
|
||||
sessionsToIgnore.add(session);
|
||||
this.selectSession();
|
||||
await this.selectSession();
|
||||
this.updateTitleArea();
|
||||
}
|
||||
}
|
||||
@@ -646,7 +647,7 @@ class ReplEvaluationResultsRenderer implements ITreeRenderer<ReplEvaluationResul
|
||||
linkDetector: this.linkDetector
|
||||
});
|
||||
if (expression.hasChildren) {
|
||||
templateData.annotation.className = 'annotation octicon octicon-info';
|
||||
templateData.annotation.className = 'annotation codicon codicon-info';
|
||||
templateData.annotation.title = nls.localize('stateCapture', "Object state is captured from first evaluation");
|
||||
}
|
||||
}
|
||||
@@ -781,7 +782,7 @@ class ReplRawObjectsRenderer implements ITreeRenderer<RawObjectReplElement, Fuzz
|
||||
|
||||
// annotation if any
|
||||
if (element.annotation) {
|
||||
templateData.annotation.className = 'annotation octicon octicon-info';
|
||||
templateData.annotation.className = 'annotation codicon codicon-info';
|
||||
templateData.annotation.title = element.annotation;
|
||||
} else {
|
||||
templateData.annotation.className = '';
|
||||
@@ -794,38 +795,33 @@ class ReplRawObjectsRenderer implements ITreeRenderer<RawObjectReplElement, Fuzz
|
||||
}
|
||||
}
|
||||
|
||||
class ReplDelegate implements IListVirtualDelegate<IReplElement> {
|
||||
class ReplDelegate extends CachedListVirtualDelegate<IReplElement> {
|
||||
|
||||
constructor(private configurationService: IConfigurationService) { }
|
||||
constructor(private configurationService: IConfigurationService) {
|
||||
super();
|
||||
}
|
||||
|
||||
getHeight(element: IReplElement): number {
|
||||
const countNumberOfLines = (str: string) => Math.max(1, (str && str.match(/\r\n|\n/g) || []).length);
|
||||
|
||||
// Give approximate heights. Repl has dynamic height so the tree will measure the actual height on its own.
|
||||
const config = this.configurationService.getValue<IDebugConfiguration>('debug');
|
||||
const fontSize = config.console.fontSize;
|
||||
const rowHeight = Math.ceil(1.4 * fontSize);
|
||||
const wordWrap = config.console.wordWrap;
|
||||
if (!wordWrap) {
|
||||
return rowHeight;
|
||||
|
||||
if (!config.console.wordWrap) {
|
||||
return Math.ceil(1.4 * config.console.fontSize);
|
||||
}
|
||||
|
||||
// In order to keep scroll position we need to give a good approximation to the tree
|
||||
// For every 150 characters increase the number of lines needed
|
||||
if (element instanceof ReplEvaluationResult) {
|
||||
return super.getHeight(element);
|
||||
}
|
||||
|
||||
protected estimateHeight(element: IReplElement): number {
|
||||
const config = this.configurationService.getValue<IDebugConfiguration>('debug');
|
||||
const rowHeight = Math.ceil(1.4 * config.console.fontSize);
|
||||
const countNumberOfLines = (str: string) => Math.max(1, (str && str.match(/\r\n|\n/g) || []).length);
|
||||
const hasValue = (e: any): e is { value: string } => typeof e.value === 'string';
|
||||
|
||||
// Calculate a rough overestimation for the height
|
||||
// For every 30 characters increase the number of lines needed
|
||||
if (hasValue(element)) {
|
||||
let value = element.value;
|
||||
|
||||
if (element.hasChildren) {
|
||||
return rowHeight;
|
||||
}
|
||||
|
||||
let valueRows = value ? (countNumberOfLines(value) + Math.floor(value.length / 150)) : 0;
|
||||
return rowHeight * valueRows;
|
||||
}
|
||||
|
||||
if (element instanceof SimpleReplElement || element instanceof ReplEvaluationInput) {
|
||||
let value = element.value;
|
||||
let valueRows = countNumberOfLines(value) + Math.floor(value.length / 150);
|
||||
let valueRows = countNumberOfLines(value) + Math.floor(value.length / 30);
|
||||
|
||||
return valueRows * rowHeight;
|
||||
}
|
||||
@@ -851,7 +847,7 @@ class ReplDelegate implements IListVirtualDelegate<IReplElement> {
|
||||
return ReplRawObjectsRenderer.ID;
|
||||
}
|
||||
|
||||
hasDynamicHeight?(element: IReplElement): boolean {
|
||||
hasDynamicHeight(element: IReplElement): boolean {
|
||||
// Empty elements should not have dynamic height since they will be invisible
|
||||
return element.toString().length > 0;
|
||||
}
|
||||
@@ -919,7 +915,7 @@ class AcceptReplInputAction extends EditorAction {
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise<void> {
|
||||
SuggestController.get(editor).acceptSelectedSuggestion();
|
||||
SuggestController.get(editor).acceptSelectedSuggestion(false, true);
|
||||
accessor.get(IPrivateReplService).acceptReplInput();
|
||||
}
|
||||
}
|
||||
@@ -941,7 +937,7 @@ class FilterReplAction extends EditorAction {
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise<void> {
|
||||
SuggestController.get(editor).acceptSelectedSuggestion();
|
||||
SuggestController.get(editor).acceptSelectedSuggestion(false, true);
|
||||
accessor.get(IPrivateReplService).focusRepl();
|
||||
}
|
||||
}
|
||||
@@ -984,7 +980,7 @@ class SelectReplActionViewItem extends FocusSessionActionViewItem {
|
||||
class SelectReplAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.debug.selectRepl';
|
||||
static LABEL = nls.localize('selectRepl', "Select Debug Console");
|
||||
static readonly LABEL = nls.localize('selectRepl', "Select Debug Console");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@@ -993,12 +989,12 @@ class SelectReplAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(session: IDebugSession): Promise<any> {
|
||||
async run(session: IDebugSession): Promise<any> {
|
||||
// If session is already the focused session we need to manualy update the tree since view model will not send a focused change event
|
||||
if (session && session.state !== State.Inactive && session !== this.debugService.getViewModel().focusedSession) {
|
||||
this.debugService.focusStackFrame(undefined, undefined, session, true);
|
||||
await this.debugService.focusStackFrame(undefined, undefined, session, true);
|
||||
} else {
|
||||
this.replService.selectSession(session);
|
||||
await this.replService.selectSession(session);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
@@ -1007,7 +1003,7 @@ class SelectReplAction extends Action {
|
||||
|
||||
export class ClearReplAction extends Action {
|
||||
static readonly ID = 'workbench.debug.panel.action.clearReplAction';
|
||||
static LABEL = nls.localize('clearRepl', "Clear Console");
|
||||
static readonly LABEL = nls.localize('clearRepl', "Clear Console");
|
||||
|
||||
constructor(id: string, label: string,
|
||||
@IPanelService private readonly panelService: IPanelService
|
||||
@@ -1015,11 +1011,9 @@ export class ClearReplAction extends Action {
|
||||
super(id, label, 'debug-action codicon-clear-all');
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
const repl = <Repl>this.panelService.openPanel(REPL_ID);
|
||||
repl.clearRepl();
|
||||
await repl.clearRepl();
|
||||
aria.status(nls.localize('debugConsoleCleared', "Debug console was cleared"));
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IDebugService, State } from 'vs/workbench/contrib/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, createStyleSheet } from 'vs/base/browser/dom';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
// colors for theming
|
||||
|
||||
@@ -56,7 +57,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
|
||||
protected updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = this.layoutService.getContainer(Parts.STATUSBAR_PART);
|
||||
const container = assertIsDefined(this.layoutService.getContainer(Parts.STATUSBAR_PART));
|
||||
if (isStatusbarInDebugMode(this.debugService)) {
|
||||
addClass(container, 'debugging');
|
||||
} else {
|
||||
@@ -65,7 +66,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
|
||||
|
||||
// Container Colors
|
||||
const backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND));
|
||||
container.style.backgroundColor = backgroundColor;
|
||||
container.style.backgroundColor = backgroundColor || '';
|
||||
container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND));
|
||||
|
||||
// Border Color
|
||||
@@ -108,7 +109,7 @@ export function isStatusbarInDebugMode(debugService: IDebugService): boolean {
|
||||
}
|
||||
|
||||
const session = debugService.getViewModel().focusedSession;
|
||||
const isRunningWithoutDebug = session && session.configuration && session.configuration.noDebug;
|
||||
const isRunningWithoutDebug = session?.configuration?.noDebug;
|
||||
if (isRunningWithoutDebug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
@@ -57,28 +56,27 @@ export class VariablesView extends ViewletPanel {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService);
|
||||
|
||||
// Use scheduler to prevent unnecessary flashing
|
||||
this.onFocusStackFrameScheduler = new RunOnceScheduler(() => {
|
||||
this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => {
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
|
||||
this.needsRefresh = false;
|
||||
if (stackFrame && this.savedViewState) {
|
||||
this.tree.setInput(this.debugService.getViewModel(), this.savedViewState).then(null, onUnexpectedError);
|
||||
await this.tree.setInput(this.debugService.getViewModel(), this.savedViewState);
|
||||
this.savedViewState = undefined;
|
||||
} else {
|
||||
if (!stackFrame) {
|
||||
// We have no stackFrame, save tree state before it is cleared
|
||||
this.savedViewState = this.tree.getViewState();
|
||||
}
|
||||
this.tree.updateChildren().then(() => {
|
||||
if (stackFrame) {
|
||||
stackFrame.getScopes().then(scopes => {
|
||||
// Expand the first scope if it is not expensive and if there is no expansion state (all are collapsed)
|
||||
if (scopes.every(s => this.tree.getNode(s).collapsed) && scopes.length > 0 && !scopes[0].expensive) {
|
||||
this.tree.expand(scopes[0]).then(undefined, onUnexpectedError);
|
||||
}
|
||||
});
|
||||
await this.tree.updateChildren();
|
||||
if (stackFrame) {
|
||||
const scopes = await stackFrame.getScopes();
|
||||
// Expand the first scope if it is not expensive and if there is no expansion state (all are collapsed)
|
||||
if (scopes.every(s => this.tree.getNode(s).collapsed) && scopes.length > 0 && !scopes[0].expensive) {
|
||||
this.tree.expand(scopes[0]);
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
|
||||
}
|
||||
}, 400);
|
||||
}
|
||||
@@ -96,12 +94,14 @@ export class VariablesView extends ViewletPanel {
|
||||
keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e }
|
||||
});
|
||||
|
||||
this.tree.setInput(this.debugService.getViewModel()).then(null, onUnexpectedError);
|
||||
this.tree.setInput(this.debugService.getViewModel());
|
||||
|
||||
CONTEXT_VARIABLES_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer');
|
||||
this.toolbar.setActions([collapseAction])();
|
||||
if (this.toolbar) {
|
||||
const collapseAction = new CollapseAction(this.tree, true, 'explorer-action collapse-explorer');
|
||||
this.toolbar.setActions([collapseAction])();
|
||||
}
|
||||
this.tree.updateChildren();
|
||||
|
||||
this._register(this.debugService.getViewModel().onDidFocusStackFrame(sf => {
|
||||
|
||||
@@ -24,7 +24,6 @@ import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
@@ -71,13 +70,15 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
dnd: new WatchExpressionsDragAndDrop(this.debugService),
|
||||
});
|
||||
|
||||
this.tree.setInput(this.debugService).then(undefined, onUnexpectedError);
|
||||
this.tree.setInput(this.debugService);
|
||||
CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.tree.contextKeyService);
|
||||
|
||||
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([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])();
|
||||
if (this.toolbar) {
|
||||
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([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction])();
|
||||
}
|
||||
|
||||
this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e)));
|
||||
@@ -152,7 +153,7 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
return Promise.resolve();
|
||||
}));
|
||||
if (!expression.hasChildren) {
|
||||
actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value, 'watch', this.debugService));
|
||||
actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, expression.value, 'watch'));
|
||||
}
|
||||
actions.push(new Separator());
|
||||
|
||||
@@ -166,7 +167,7 @@ export class WatchExpressionsView extends ViewletPanel {
|
||||
if (element instanceof Variable) {
|
||||
const variable = element as Variable;
|
||||
if (!variable.hasChildren) {
|
||||
actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable, 'watch', this.debugService));
|
||||
actions.push(this.instantiationService.createInstance(CopyValueAction, CopyValueAction.ID, CopyValueAction.LABEL, variable, 'watch'));
|
||||
}
|
||||
actions.push(new Separator());
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ export interface IDebugSession extends ITreeElement {
|
||||
readonly configuration: IConfig;
|
||||
readonly unresolvedConfiguration: IConfig | undefined;
|
||||
readonly state: State;
|
||||
readonly root: IWorkspaceFolder;
|
||||
readonly root: IWorkspaceFolder | undefined;
|
||||
readonly parentSession: IDebugSession | undefined;
|
||||
readonly subId: string | undefined;
|
||||
|
||||
@@ -467,6 +467,7 @@ export interface IDebugConfiguration {
|
||||
};
|
||||
focusWindowOnBreak: boolean;
|
||||
onTaskErrors: 'debugAnyway' | 'showErrors' | 'prompt';
|
||||
showBreakpointsInOverviewRuler: boolean;
|
||||
}
|
||||
|
||||
export interface IGlobalConfig {
|
||||
@@ -732,7 +733,7 @@ export interface IDebugService {
|
||||
/**
|
||||
* Sets the focused stack frame and evaluates all expressions against the newly focused stack frame,
|
||||
*/
|
||||
focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): void;
|
||||
focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise<void>;
|
||||
|
||||
/**
|
||||
* Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes.
|
||||
|
||||
@@ -27,7 +27,7 @@ import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export class ExpressionContainer implements IExpressionContainer {
|
||||
|
||||
public static allValues = new Map<string, string>();
|
||||
public static readonly allValues = new Map<string, string>();
|
||||
// Use chunks to support variable paging #9537
|
||||
private static readonly BASE_CHUNK_SIZE = 100;
|
||||
|
||||
@@ -65,7 +65,7 @@ export class ExpressionContainer implements IExpressionContainer {
|
||||
|
||||
private async doGetChildren(): Promise<IExpression[]> {
|
||||
if (!this.hasChildren) {
|
||||
return Promise.resolve([]);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!this.getChildrenInChunks) {
|
||||
@@ -114,13 +114,16 @@ export class ExpressionContainer implements IExpressionContainer {
|
||||
return !!this.reference && this.reference > 0;
|
||||
}
|
||||
|
||||
private fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise<Variable[]> {
|
||||
return this.session!.variables(this.reference || 0, this.threadId, filter, start, count).then(response => {
|
||||
private async fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise<Variable[]> {
|
||||
try {
|
||||
const response = await this.session!.variables(this.reference || 0, this.threadId, filter, start, count);
|
||||
return response && response.body && response.body.variables
|
||||
? distinct(response.body.variables.filter(v => !!v && isString(v.name)), (v: DebugProtocol.Variable) => v.name).map((v: DebugProtocol.Variable) =>
|
||||
new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type))
|
||||
: [];
|
||||
}, (e: Error) => [new Variable(this.session, this.threadId, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]);
|
||||
} catch (e) {
|
||||
return [new Variable(this.session, this.threadId, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)];
|
||||
}
|
||||
}
|
||||
|
||||
// The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked.
|
||||
@@ -172,7 +175,7 @@ export class ExpressionContainer implements IExpressionContainer {
|
||||
}
|
||||
|
||||
export class Expression extends ExpressionContainer implements IExpression {
|
||||
static DEFAULT_VALUE = nls.localize('notAvailable', "not available");
|
||||
static readonly DEFAULT_VALUE = nls.localize('notAvailable', "not available");
|
||||
|
||||
public available: boolean;
|
||||
|
||||
@@ -221,7 +224,7 @@ export class Variable extends ExpressionContainer implements IExpression {
|
||||
|
||||
async setVariable(value: string): Promise<any> {
|
||||
if (!this.session) {
|
||||
return Promise.resolve(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -313,18 +316,17 @@ export class StackFrame implements IStackFrame {
|
||||
return (from > 0 ? '...' : '') + this.source.uri.path.substr(from);
|
||||
}
|
||||
|
||||
getMostSpecificScopes(range: IRange): Promise<IScope[]> {
|
||||
return this.getScopes().then(scopes => {
|
||||
scopes = scopes.filter(s => !s.expensive);
|
||||
const haveRangeInfo = scopes.some(s => !!s.range);
|
||||
if (!haveRangeInfo) {
|
||||
return scopes;
|
||||
}
|
||||
async getMostSpecificScopes(range: IRange): Promise<IScope[]> {
|
||||
const scopes = await this.getScopes();
|
||||
const nonExpensiveScopes = scopes.filter(s => !s.expensive);
|
||||
const haveRangeInfo = nonExpensiveScopes.some(s => !!s.range);
|
||||
if (!haveRangeInfo) {
|
||||
return nonExpensiveScopes;
|
||||
}
|
||||
|
||||
const scopesContainingRange = scopes.filter(scope => scope.range && Range.containsRange(scope.range, range))
|
||||
.sort((first, second) => (first.range!.endLineNumber - first.range!.startLineNumber) - (second.range!.endLineNumber - second.range!.startLineNumber));
|
||||
return scopesContainingRange.length ? scopesContainingRange : scopes;
|
||||
});
|
||||
const scopesContainingRange = nonExpensiveScopes.filter(scope => scope.range && Range.containsRange(scope.range, range))
|
||||
.sort((first, second) => (first.range!.endLineNumber - first.range!.startLineNumber) - (second.range!.endLineNumber - second.range!.startLineNumber));
|
||||
return scopesContainingRange.length ? scopesContainingRange : nonExpensiveScopes;
|
||||
}
|
||||
|
||||
restart(): Promise<void> {
|
||||
@@ -342,9 +344,11 @@ export class StackFrame implements IStackFrame {
|
||||
return sourceToString === UNKNOWN_SOURCE_LABEL ? this.name : `${this.name} (${sourceToString})`;
|
||||
}
|
||||
|
||||
openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<ITextEditor | undefined> {
|
||||
return !this.source.available ? Promise.resolve(undefined) :
|
||||
this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned);
|
||||
async openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<ITextEditor | undefined> {
|
||||
if (this.source.available) {
|
||||
return this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
equals(other: IStackFrame): boolean {
|
||||
@@ -399,23 +403,21 @@ export class Thread implements IThread {
|
||||
* Only fetches the first stack frame for performance reasons. Calling this method consecutive times
|
||||
* gets the remainder of the call stack.
|
||||
*/
|
||||
fetchCallStack(levels = 20): Promise<void> {
|
||||
if (!this.stopped) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const start = this.callStack.length;
|
||||
return this.getCallStackImpl(start, levels).then(callStack => {
|
||||
async fetchCallStack(levels = 20): Promise<void> {
|
||||
if (this.stopped) {
|
||||
const start = this.callStack.length;
|
||||
const callStack = await this.getCallStackImpl(start, levels);
|
||||
if (start < this.callStack.length) {
|
||||
// Set the stack frames for exact position we requested. To make sure no concurrent requests create duplicate stack frames #30660
|
||||
this.callStack.splice(start, this.callStack.length - start);
|
||||
}
|
||||
this.callStack = this.callStack.concat(callStack || []);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getCallStackImpl(startFrame: number, levels: number): Promise<IStackFrame[]> {
|
||||
return this.session.stackTrace(this.threadId, startFrame, levels).then(response => {
|
||||
private async getCallStackImpl(startFrame: number, levels: number): Promise<IStackFrame[]> {
|
||||
try {
|
||||
const response = await this.session.stackTrace(this.threadId, startFrame, levels);
|
||||
if (!response || !response.body) {
|
||||
return [];
|
||||
}
|
||||
@@ -434,13 +436,13 @@ export class Thread implements IThread {
|
||||
rsf.endColumn || rsf.column
|
||||
), startFrame + index);
|
||||
});
|
||||
}, (err: Error) => {
|
||||
} catch (err) {
|
||||
if (this.stoppedDetails) {
|
||||
this.stoppedDetails.framesErrorMessage = err.message;
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,8 +619,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint {
|
||||
}
|
||||
|
||||
get column(): number | undefined {
|
||||
// Only respect the column if the user explictly set the column to have an inline breakpoint
|
||||
return this.verified && this.data && typeof this.data.column === 'number' && typeof this._column === 'number' ? this.data.column : this._column;
|
||||
return this.verified && this.data && typeof this.data.column === 'number' ? this.data.column : this._column;
|
||||
}
|
||||
|
||||
get message(): string | undefined {
|
||||
|
||||
@@ -4,32 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { IConfig, IDebuggerContribution, IDebugService, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IConfig, IDebuggerContribution, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { isAbsolute } from 'vs/base/common/path';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
|
||||
const _formatPIIRegexp = /{([^}]+)}/g;
|
||||
|
||||
export function startDebugging(debugService: IDebugService, historyService: IHistoryService, noDebug: boolean, ): Promise<boolean> {
|
||||
const configurationManager = debugService.getConfigurationManager();
|
||||
let launch = configurationManager.selectedConfiguration.launch;
|
||||
if (!launch || launch.getConfigurationNames().length === 0) {
|
||||
const rootUri = historyService.getLastActiveWorkspaceRoot();
|
||||
launch = configurationManager.getLaunch(rootUri);
|
||||
if (!launch || launch.getConfigurationNames().length === 0) {
|
||||
const launches = configurationManager.getLaunches();
|
||||
launch = first(launches, l => !!(l && l.getConfigurationNames().length), launch);
|
||||
}
|
||||
|
||||
configurationManager.selectConfiguration(launch);
|
||||
}
|
||||
|
||||
return debugService.startDebugging(launch, undefined, { noDebug });
|
||||
}
|
||||
|
||||
export function formatPII(value: string, excludePII: boolean, args: { [key: string]: string }): string {
|
||||
return value.replace(_formatPIIRegexp, function (match, group) {
|
||||
if (excludePII && group.length > 0 && group[0] !== '_') {
|
||||
@@ -196,6 +177,9 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA:
|
||||
case 'setBreakpoints':
|
||||
fixSourcePath(true, (<DebugProtocol.SetBreakpointsArguments>request.arguments).source);
|
||||
break;
|
||||
case 'breakpointLocations':
|
||||
fixSourcePath(true, (<DebugProtocol.BreakpointLocationsArguments>request.arguments).source);
|
||||
break;
|
||||
case 'source':
|
||||
fixSourcePath(true, (<DebugProtocol.SourceArguments>request.arguments).source);
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,6 @@ import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHo
|
||||
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
|
||||
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
|
||||
export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
@@ -20,7 +19,7 @@ export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
super(mainProcessService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise<void> {
|
||||
// TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060)
|
||||
return this.electronService.openExtensionDevelopmentHostWindow(args, env);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ export class SocketDebugAdapter extends StreamDebugAdapter {
|
||||
this.socket.end();
|
||||
this.socket = undefined;
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +177,6 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter {
|
||||
if (options.env) {
|
||||
env = objects.mixin(env, options.env);
|
||||
}
|
||||
delete env.VSCODE_PREVENT_FOREIGN_INSPECT;
|
||||
|
||||
if (command === 'node') {
|
||||
if (Array.isArray(args) && args.length > 0) {
|
||||
|
||||
@@ -148,7 +148,7 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments
|
||||
if (value === null) {
|
||||
command += `set "${key}=" && `;
|
||||
} else {
|
||||
value = value.replace(/[\^\&]/g, s => `^${s}`);
|
||||
value = value.replace(/[\^\&\|\<\>]/g, s => `^${s}`);
|
||||
command += `set "${key}=${value}" && `;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ suite('Debug - ANSI Handling', () => {
|
||||
*/
|
||||
setup(() => {
|
||||
model = new DebugModel([], [], [], [], [], <any>{ isDirty: (e: any) => false });
|
||||
session = new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
|
||||
session = new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
|
||||
|
||||
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService();
|
||||
linkDetector = instantiationService.createInstance(LinkDetector);
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IBreakpointUpdateData, IDebugSessionOptions } from 'vs/workbench/contri
|
||||
import { NullOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession {
|
||||
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
|
||||
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
|
||||
}
|
||||
|
||||
suite('Debug - Model', () => {
|
||||
|
||||
@@ -40,7 +40,8 @@ export class MockDebugService implements IDebugService {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public focusStackFrame(focusedStackFrame: IStackFrame): void {
|
||||
public focusStackFrame(focusedStackFrame: IStackFrame): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
sendAllBreakpoints(session?: IDebugSession): Promise<any> {
|
||||
|
||||
Reference in New Issue
Block a user