mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-28 09:35:38 -05:00
Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f (#7282)
* Merge from vscode 1eb87b0e9ce9886afeaecec22b31abd0d9b7939f * fix various icon issues * fix preview features
This commit is contained in:
@@ -0,0 +1,567 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import severity from 'vs/base/common/severity';
|
||||
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 { 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 { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
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';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
interface IBreakpointDecoration {
|
||||
decorationId: string;
|
||||
breakpoint: IBreakpoint;
|
||||
range: Range;
|
||||
inlineWidget?: InlineBreakpointWidget;
|
||||
}
|
||||
|
||||
const breakpointHelperDecoration: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-hint',
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray<IBreakpoint>, debugService: IDebugService): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
const result: { range: Range; options: IModelDecorationOptions; }[] = [];
|
||||
breakpoints.forEach((breakpoint) => {
|
||||
if (breakpoint.lineNumber <= model.getLineCount()) {
|
||||
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
|
||||
const range = model.validateRange(
|
||||
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
|
||||
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
|
||||
);
|
||||
|
||||
result.push({
|
||||
options: getBreakpointDecorationOptions(model, breakpoint, debugService),
|
||||
range
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, debugService: IDebugService): IModelDecorationOptions {
|
||||
const { className, message } = getBreakpointMessageAndClassName(debugService, breakpoint);
|
||||
let glyphMarginHoverMessage: MarkdownString | undefined;
|
||||
|
||||
if (message) {
|
||||
if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
const modeId = model.getLanguageIdentifier().language;
|
||||
glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message);
|
||||
} else {
|
||||
glyphMarginHoverMessage = new MarkdownString().appendText(message);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
glyphMarginClassName: className,
|
||||
glyphMarginHoverMessage,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-placeholder` : undefined
|
||||
};
|
||||
}
|
||||
|
||||
async function createCandidateDecorations(model: ITextModel, breakpointDecorations: IBreakpointDecoration[], debugService: IDebugService): Promise<{ range: Range; options: IModelDecorationOptions; breakpoint: IBreakpoint | undefined }[]> {
|
||||
const lineNumbers = distinct(breakpointDecorations.map(bpd => bpd.range.startLineNumber));
|
||||
const result: { range: Range; options: IModelDecorationOptions; breakpoint: IBreakpoint | undefined }[] = [];
|
||||
const session = debugService.getViewModel().focusedSession;
|
||||
if (session && session.capabilities.supportsBreakpointLocationsRequest) {
|
||||
await Promise.all(lineNumbers.map(async lineNumber => {
|
||||
const positions = await session.breakpointsLocations(model.uri, lineNumber);
|
||||
if (positions.length > 1) {
|
||||
// Do not render candidates if there is only one, since it is already covered by the line breakpoint
|
||||
positions.forEach(p => {
|
||||
const range = new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1);
|
||||
const breakpointAtPosition = breakpointDecorations.filter(bpd => bpd.range.equalsRange(range)).pop();
|
||||
if (breakpointAtPosition && breakpointAtPosition.inlineWidget) {
|
||||
// Space already occupied, do not render candidate.
|
||||
return;
|
||||
}
|
||||
result.push({
|
||||
range,
|
||||
options: {
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
beforeContentClassName: `debug-breakpoint-placeholder`
|
||||
},
|
||||
breakpoint: breakpointAtPosition ? breakpointAtPosition.breakpoint : undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
class BreakpointEditorContribution implements IBreakpointEditorContribution {
|
||||
|
||||
private breakpointHintDecoration: string[] = [];
|
||||
private breakpointWidget: BreakpointWidget | undefined;
|
||||
private breakpointWidgetVisible: IContextKey<boolean>;
|
||||
private toDispose: IDisposable[] = [];
|
||||
private ignoreDecorationsChangedEvent = false;
|
||||
private ignoreBreakpointsChangeEvent = false;
|
||||
private breakpointDecorations: IBreakpointDecoration[] = [];
|
||||
private candidateDecorations: { decorationId: string, inlineWidget: InlineBreakpointWidget }[] = [];
|
||||
private setDecorationsScheduler: RunOnceScheduler;
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
) {
|
||||
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;
|
||||
const model = this.editor.getModel();
|
||||
if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model);
|
||||
const lineNumber = e.target.position.lineNumber;
|
||||
const uri = model.uri;
|
||||
|
||||
if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) {
|
||||
if (!canSetBreakpoints) {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = { x: e.event.posx, y: e.event.posy };
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber, uri });
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber),
|
||||
getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined
|
||||
});
|
||||
} else {
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber });
|
||||
|
||||
if (breakpoints.length) {
|
||||
// Show the dialog if there is a potential condition to be accidently lost.
|
||||
// Do not show dialog on linux due to electron issue freezing the mouse #50026
|
||||
if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) {
|
||||
const logPoint = breakpoints.every(bp => !!bp.logMessage);
|
||||
const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
const disable = breakpoints.some(bp => bp.enabled);
|
||||
|
||||
const enabling = nls.localize('breakpointHasConditionDisabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
const disabling = nls.localize('breakpointHasConditionEnabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
|
||||
const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [
|
||||
nls.localize('removeLogPoint', "Remove {0}", breakpointType),
|
||||
nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType),
|
||||
nls.localize('cancel', "Cancel")
|
||||
], { cancelId: 2 });
|
||||
|
||||
if (choice === 0) {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
if (choice === 1) {
|
||||
breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp));
|
||||
}
|
||||
} else {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
} else if (canSetBreakpoints) {
|
||||
this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
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) &&
|
||||
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (!data.isAfterLines) {
|
||||
showBreakpointHintAtLineNumber = e.target.position.lineNumber;
|
||||
}
|
||||
}
|
||||
this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber);
|
||||
}));
|
||||
this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => {
|
||||
this.ensureBreakpointHintDecoration(-1);
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.editor.onDidChangeModel(async () => {
|
||||
this.closeBreakpointWidget();
|
||||
await this.setDecorations();
|
||||
}));
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(async () => {
|
||||
if (!this.ignoreBreakpointsChangeEvent && !this.setDecorationsScheduler.isScheduled()) {
|
||||
this.setDecorationsScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidChangeModelDecorations(() => this.onModelDecorationsChanged()));
|
||||
}
|
||||
|
||||
private getContextMenuActions(breakpoints: ReadonlyArray<IBreakpoint>, uri: uri, lineNumber: number, column?: number): Array<IAction | ContextSubMenu> {
|
||||
const actions: Array<IAction | ContextSubMenu> = [];
|
||||
if (breakpoints.length === 1) {
|
||||
const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService));
|
||||
actions.push(new Action(
|
||||
'workbench.debug.action.editBreakpointAction',
|
||||
nls.localize('editBreakpoint', "Edit {0}...", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(breakpoints[0].lineNumber, breakpoints[0].column))
|
||||
));
|
||||
|
||||
actions.push(new Action(
|
||||
`workbench.debug.viewlet.action.toggleBreakpoint`,
|
||||
breakpoints[0].enabled ? nls.localize('disableBreakpoint', "Disable {0}", breakpointType) : nls.localize('enableBreakpoint', "Enable {0}", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!breakpoints[0].enabled, breakpoints[0])
|
||||
));
|
||||
} else if (breakpoints.length > 1) {
|
||||
const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1);
|
||||
actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action(
|
||||
'removeInlineBreakpoint',
|
||||
bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.removeBreakpoints(bp.getId())
|
||||
))));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp =>
|
||||
new Action('editBreakpoint',
|
||||
bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(bp.lineNumber, bp.column))
|
||||
)
|
||||
)));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action(
|
||||
bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint',
|
||||
bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint"))
|
||||
: (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp)
|
||||
))));
|
||||
} else {
|
||||
actions.push(new Action(
|
||||
'addBreakpoint',
|
||||
nls.localize('addBreakpoint', "Add Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.addBreakpoints(uri, [{ lineNumber, column }], `debugEditorContextMenu`)
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addConditionalBreakpoint',
|
||||
nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(lineNumber, column))
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addLogPoint',
|
||||
nls.localize('addLogPoint', "Add Logpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.showBreakpointWidget(lineNumber, column, BreakpointWidgetContext.LOG_MESSAGE))
|
||||
));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private marginFreeFromNonDebugDecorations(line: number): boolean {
|
||||
const decorations = this.editor.getLineDecorations(line);
|
||||
if (decorations) {
|
||||
for (const { options } of decorations) {
|
||||
if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void {
|
||||
const newDecoration: IModelDeltaDecoration[] = [];
|
||||
if (showBreakpointHintAtLineNumber !== -1) {
|
||||
newDecoration.push({
|
||||
options: breakpointHelperDecoration,
|
||||
range: {
|
||||
startLineNumber: showBreakpointHintAtLineNumber,
|
||||
startColumn: 1,
|
||||
endLineNumber: showBreakpointHintAtLineNumber,
|
||||
endColumn: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration);
|
||||
}
|
||||
|
||||
private async setDecorations(): Promise<void> {
|
||||
if (!this.editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeCodeEditor = this.editor;
|
||||
const model = activeCodeEditor.getModel();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri });
|
||||
const desiredBreakpointDecorations = createBreakpointDecorations(model, breakpoints, this.debugService);
|
||||
|
||||
try {
|
||||
this.ignoreDecorationsChangedEvent = true;
|
||||
|
||||
// Set breakpoint decorations
|
||||
const decorationIds = activeCodeEditor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), desiredBreakpointDecorations);
|
||||
this.breakpointDecorations.forEach(bpd => {
|
||||
if (bpd.inlineWidget) {
|
||||
bpd.inlineWidget.dispose();
|
||||
}
|
||||
});
|
||||
this.breakpointDecorations = decorationIds.map((decorationId, index) => {
|
||||
let inlineWidget: InlineBreakpointWidget | undefined = undefined;
|
||||
const breakpoint = breakpoints[index];
|
||||
if (breakpoint.column) {
|
||||
inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, desiredBreakpointDecorations[index].options.glyphMarginClassName, breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([breakpoint], activeCodeEditor.getModel().uri, breakpoint.lineNumber, breakpoint.column));
|
||||
}
|
||||
|
||||
return {
|
||||
decorationId,
|
||||
breakpoint,
|
||||
range: desiredBreakpointDecorations[index].range,
|
||||
inlineWidget
|
||||
};
|
||||
});
|
||||
|
||||
} finally {
|
||||
this.ignoreDecorationsChangedEvent = false;
|
||||
}
|
||||
|
||||
// Set breakpoint candidate decorations
|
||||
const desiredCandidateDecorations = await createCandidateDecorations(this.editor.getModel(), this.breakpointDecorations, this.debugService);
|
||||
const candidateDecorationIds = this.editor.deltaDecorations(this.candidateDecorations.map(c => c.decorationId), desiredCandidateDecorations);
|
||||
this.candidateDecorations.forEach(candidate => {
|
||||
candidate.inlineWidget.dispose();
|
||||
});
|
||||
this.candidateDecorations = candidateDecorationIds.map((decorationId, index) => {
|
||||
const candidate = desiredCandidateDecorations[index];
|
||||
const cssClass = candidate.breakpoint ? undefined : 'debug-breakpoint-disabled';
|
||||
const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, cssClass, candidate.breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn));
|
||||
|
||||
return {
|
||||
decorationId,
|
||||
inlineWidget
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async onModelDecorationsChanged(): Promise<void> {
|
||||
if (this.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent || !this.editor.hasModel()) {
|
||||
// I have no decorations
|
||||
return;
|
||||
}
|
||||
let somethingChanged = false;
|
||||
const model = this.editor.getModel();
|
||||
this.breakpointDecorations.forEach(breakpointDecoration => {
|
||||
if (somethingChanged) {
|
||||
return;
|
||||
}
|
||||
const newBreakpointRange = model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
if (!somethingChanged) {
|
||||
// nothing to do, my decorations did not change.
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new Map<string, IBreakpointUpdateData>();
|
||||
for (let i = 0, len = this.breakpointDecorations.length; i < len; i++) {
|
||||
const breakpointDecoration = this.breakpointDecorations[i];
|
||||
const decorationRange = model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
// check if the line got deleted.
|
||||
if (decorationRange) {
|
||||
// since we know it is collapsed, it cannot grow to multiple lines
|
||||
if (breakpointDecoration.breakpoint) {
|
||||
data.set(breakpointDecoration.breakpoint.getId(), {
|
||||
lineNumber: decorationRange.startLineNumber,
|
||||
column: breakpointDecoration.breakpoint.column ? decorationRange.startColumn : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.ignoreBreakpointsChangeEvent = true;
|
||||
await this.debugService.updateBreakpoints(model.uri, data, true);
|
||||
} finally {
|
||||
this.ignoreBreakpointsChangeEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
// breakpoint widget
|
||||
showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
|
||||
this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, column, context);
|
||||
this.breakpointWidget.show({ lineNumber, column: 1 });
|
||||
this.breakpointWidgetVisible.set(true);
|
||||
}
|
||||
|
||||
closeBreakpointWidget(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
this.breakpointWidget = undefined;
|
||||
this.breakpointWidgetVisible.reset();
|
||||
this.editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
this.editor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), []);
|
||||
dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
class InlineBreakpointWidget implements IContentWidget, IDisposable {
|
||||
|
||||
// editor.IContentWidget.allowEditorOverflow
|
||||
allowEditorOverflow = false;
|
||||
suppressMouseDown = true;
|
||||
|
||||
private domNode!: HTMLElement;
|
||||
private range: Range | null;
|
||||
private toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly editor: IActiveCodeEditor,
|
||||
private readonly decorationId: string,
|
||||
cssClass: string | null | undefined,
|
||||
private readonly breakpoint: IBreakpoint | undefined,
|
||||
private readonly debugService: IDebugService,
|
||||
private readonly contextMenuService: IContextMenuService,
|
||||
private readonly getContextMenuActions: () => ReadonlyArray<IAction | ContextSubMenu>
|
||||
) {
|
||||
this.range = this.editor.getModel().getDecorationRange(decorationId);
|
||||
this.toDispose.push(this.editor.onDidChangeModelDecorations(() => {
|
||||
const model = this.editor.getModel();
|
||||
const range = model.getDecorationRange(this.decorationId);
|
||||
if (this.range && !this.range.equalsRange(range)) {
|
||||
this.range = range;
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
}));
|
||||
this.create(cssClass);
|
||||
|
||||
this.editor.addContentWidget(this);
|
||||
this.editor.layoutContentWidget(this);
|
||||
}
|
||||
|
||||
private create(cssClass: string | null | undefined): void {
|
||||
this.domNode = $('.inline-breakpoint-widget');
|
||||
if (cssClass) {
|
||||
this.domNode.classList.add(cssClass);
|
||||
}
|
||||
this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, async e => {
|
||||
if (this.breakpoint) {
|
||||
await this.debugService.removeBreakpoints(this.breakpoint.getId());
|
||||
} else {
|
||||
await this.debugService.addBreakpoints(this.editor.getModel().uri, [{ lineNumber: this.range!.startLineNumber, column: this.range!.startColumn }], 'debugEditorInlineWidget');
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CONTEXT_MENU, async e => {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const anchor = { x: event.posx, y: event.posy };
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions(),
|
||||
getActionsContext: () => this.breakpoint
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@memoize
|
||||
getId(): string {
|
||||
return generateUuid();
|
||||
}
|
||||
|
||||
getDomNode(): HTMLElement {
|
||||
return this.domNode;
|
||||
}
|
||||
|
||||
getPosition(): IContentWidgetPosition | null {
|
||||
if (!this.range) {
|
||||
return null;
|
||||
}
|
||||
// Workaround: since the content widget can not be placed before the first column we need to force the left position
|
||||
dom.toggleClass(this.domNode, 'line-start', this.range.startColumn === 1);
|
||||
|
||||
return {
|
||||
position: { lineNumber: this.range.startLineNumber, column: this.range.startColumn - 1 },
|
||||
preference: [ContentWidgetPositionPreference.EXACT]
|
||||
};
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.editor.removeContentWidget(this);
|
||||
dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(BreakpointEditorContribution);
|
||||
@@ -13,7 +13,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, CONTEXT_IN_BREAKPOINT_WIDGET, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, CONTEXT_IN_BREAKPOINT_WIDGET, IBreakpointUpdateData, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -37,7 +37,7 @@ import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
const $ = dom.$;
|
||||
const IPrivateBreakpointWidgetService = createDecorator<IPrivateBreakpointWidgetService>('privateBreakopintWidgetService');
|
||||
const IPrivateBreakpointWidgetService = createDecorator<IPrivateBreakpointWidgetService>('privateBreakpointWidgetService');
|
||||
export interface IPrivateBreakpointWidgetService {
|
||||
_serviceBrand: undefined;
|
||||
close(success: boolean): void;
|
||||
@@ -55,7 +55,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
private logMessageInput = '';
|
||||
private breakpoint: IBreakpoint | undefined;
|
||||
|
||||
constructor(editor: ICodeEditor, private lineNumber: number, private context: Context,
|
||||
constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, private context: Context,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@@ -70,7 +70,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
const uri = model.uri;
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, uri });
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, column: this.column, uri });
|
||||
this.breakpoint = breakpoints.length ? breakpoints[0] : undefined;
|
||||
}
|
||||
|
||||
@@ -130,12 +130,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
}
|
||||
}
|
||||
|
||||
show(rangeOrPos: IRange | IPosition, heightInLines: number) {
|
||||
show(rangeOrPos: IRange | IPosition): void {
|
||||
const lineNum = this.input.getModel().getLineCount();
|
||||
super.show(rangeOrPos, lineNum + 1);
|
||||
}
|
||||
|
||||
fitHeightToContent() {
|
||||
fitHeightToContent(): void {
|
||||
const lineNum = this.input.getModel().getLineCount();
|
||||
this._relayout(lineNum + 1);
|
||||
}
|
||||
@@ -293,6 +293,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
|
||||
if (model) {
|
||||
this.debugService.addBreakpoints(model.uri, [{
|
||||
lineNumber: this.lineNumber,
|
||||
column: this.column,
|
||||
enabled: true,
|
||||
condition,
|
||||
hitCondition,
|
||||
@@ -348,7 +349,7 @@ class CloseBreakpointWidgetCommand extends EditorCommand {
|
||||
}
|
||||
|
||||
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
|
||||
const debugContribution = editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);
|
||||
const debugContribution = editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID);
|
||||
if (debugContribution) {
|
||||
// if focus is in outer editor we need to use the debug contribution to close
|
||||
return debugContribution.closeBreakpointWidget();
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -167,7 +167,7 @@ export class BreakpointsView extends ViewletPanel {
|
||||
if (editor) {
|
||||
const codeEditor = editor.getControl();
|
||||
if (isCodeEditor(codeEditor)) {
|
||||
codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -180,7 +180,7 @@ export class BreakpointsView extends ViewletPanel {
|
||||
actions.push(new Separator());
|
||||
}
|
||||
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService));
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService));
|
||||
|
||||
if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) {
|
||||
actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService));
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { DebugEditorModelManager } from 'vs/workbench/contrib/debug/browser/debugEditorModelManager';
|
||||
import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
|
||||
import * as service from 'vs/workbench/contrib/debug/browser/debugService';
|
||||
@@ -49,6 +48,7 @@ import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'
|
||||
import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl';
|
||||
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
|
||||
import { registerAndGetAmdImageURL } from 'vs/base/common/amd';
|
||||
import { DebugCallStackContribution } from 'vs/workbench/contrib/debug/browser/debugCallStackContribution';
|
||||
|
||||
class OpenDebugViewletAction extends ShowViewletAction {
|
||||
public static readonly ID = VIEWLET_ID;
|
||||
@@ -121,7 +121,7 @@ const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryEx
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View"));
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View"));
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugEditorModelManager, LifecyclePhase.Restored);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugCallStackContribution, LifecyclePhase.Restored);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually);
|
||||
|
||||
@@ -181,12 +181,12 @@ export class SelectAndStartAction extends AbstractDebugAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveBreakpointAction extends AbstractDebugAction {
|
||||
export class RemoveBreakpointAction extends Action {
|
||||
static readonly ID = 'workbench.debug.viewlet.action.removeBreakpoint';
|
||||
static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint");
|
||||
|
||||
constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
super(id, label, 'debug-action remove', debugService, keybindingService);
|
||||
constructor(id: string, label: string, @IDebugService private readonly debugService: IDebugService) {
|
||||
super(id, label, 'debug-action remove');
|
||||
}
|
||||
|
||||
public run(breakpoint: IBreakpoint): Promise<any> {
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Constants } from 'vs/editor/common/core/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';
|
||||
import { IDebugService, State } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
|
||||
interface IDebugEditorModelData {
|
||||
model: ITextModel;
|
||||
currentStackDecorations: string[];
|
||||
topStackFrameRange: Range | undefined;
|
||||
}
|
||||
|
||||
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
|
||||
export class DebugCallStackContribution implements IWorkbenchContribution {
|
||||
private modelDataMap = new Map<string, IDebugEditorModelData>();
|
||||
private toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
) {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this));
|
||||
this.modelService.getModels().forEach(model => this.onModelAdded(model));
|
||||
this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame()));
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
if (state === State.Inactive) {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.topStackFrameRange = undefined;
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onModelAdded(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr));
|
||||
|
||||
this.modelDataMap.set(modelUriStr, {
|
||||
model: model,
|
||||
currentStackDecorations: currentStackDecorations,
|
||||
topStackFrameRange: undefined
|
||||
});
|
||||
}
|
||||
|
||||
private onModelRemoved(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const data = this.modelDataMap.get(modelUriStr);
|
||||
if (data) {
|
||||
this.modelDataMap.delete(modelUriStr);
|
||||
}
|
||||
}
|
||||
|
||||
private onFocusStackFrame(): void {
|
||||
this.modelDataMap.forEach((modelData, uri) => {
|
||||
modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri));
|
||||
});
|
||||
}
|
||||
|
||||
private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] {
|
||||
const result: IModelDeltaDecoration[] = [];
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// only show decorations for the currently focused thread.
|
||||
const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1);
|
||||
|
||||
// compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame,
|
||||
// an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line).
|
||||
const callStack = stackFrame.thread.getCallStack();
|
||||
if (callStack && callStack.length && stackFrame === callStack[0]) {
|
||||
result.push({
|
||||
options: DebugCallStackContribution.TOP_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugCallStackContribution.TOP_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
|
||||
const modelData = this.modelDataMap.get(modelUriStr);
|
||||
if (modelData) {
|
||||
if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) {
|
||||
result.push({
|
||||
options: DebugCallStackContribution.TOP_STACK_FRAME_INLINE_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
modelData.topStackFrameRange = columnUntilEOLRange;
|
||||
}
|
||||
} else {
|
||||
result.push({
|
||||
options: DebugCallStackContribution.FOCUSED_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugCallStackContribution.FOCUSED_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// editor decorations
|
||||
|
||||
static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
|
||||
private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-top-stack-frame',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-focused-stack-frame',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-top-stack-frame-line',
|
||||
stickiness
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = {
|
||||
beforeContentClassName: 'debug-top-stack-frame-column'
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-focused-stack-frame-line',
|
||||
stickiness
|
||||
};
|
||||
|
||||
dispose(): void {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.model.deltaDecorations(modelData.currentStackDecorations, []);
|
||||
});
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
|
||||
this.modelDataMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const topStackFrame = theme.getColor(topStackFrameColor);
|
||||
if (topStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
}
|
||||
|
||||
const focusedStackFrame = theme.getColor(focusedStackFrameColor);
|
||||
if (focusedStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
|
||||
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
|
||||
@@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -75,7 +75,7 @@ class ConditionalBreakpointAction extends EditorAction {
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column);
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ class LogPointAction extends EditorAction {
|
||||
|
||||
const position = editor.getPosition();
|
||||
if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) {
|
||||
editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE);
|
||||
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, BreakpointWidgetContext.LOG_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { visit } from 'vs/base/common/json';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
@@ -19,32 +15,25 @@ import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, BreakpointWidgetContext } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IDebugEditorContribution, IDebugService, State, EDITOR_CONTRIBUTION_ID, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget';
|
||||
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { getHover } from 'vs/editor/contrib/hover/getHover';
|
||||
import { IEditorHoverOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget';
|
||||
import { DebugHoverWidget } from 'vs/workbench/contrib/debug/browser/debugHover';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { getHover } from 'vs/editor/contrib/hover/getHover';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
const HOVER_DELAY = 300;
|
||||
const LAUNCH_JSON_REGEX = /launch\.json$/;
|
||||
@@ -53,17 +42,14 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We
|
||||
const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added
|
||||
const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped
|
||||
|
||||
export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
class DebugEditorContribution implements IDebugEditorContribution {
|
||||
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private toDispose: IDisposable[];
|
||||
private hoverWidget: DebugHoverWidget;
|
||||
private nonDebugHoverPosition: Position | undefined;
|
||||
private hoverRange: Range | null = null;
|
||||
private mouseDown = false;
|
||||
|
||||
private breakpointHintDecoration: string[];
|
||||
private breakpointWidget: BreakpointWidget | undefined;
|
||||
private breakpointWidgetVisible: IContextKey<boolean>;
|
||||
private wordToLineNumbersMap: Map<string, Position[]> | undefined;
|
||||
|
||||
private exceptionWidget: ExceptionWidget | undefined;
|
||||
@@ -73,182 +59,21 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
) {
|
||||
this.breakpointHintDecoration = [];
|
||||
this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor);
|
||||
this.toDispose = [];
|
||||
this.registerListeners();
|
||||
this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService);
|
||||
this.updateConfigurationWidgetVisibility();
|
||||
this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {});
|
||||
this.toggleExceptionWidget();
|
||||
}
|
||||
|
||||
private getContextMenuActions(breakpoints: ReadonlyArray<IBreakpoint>, uri: uri, lineNumber: number): Array<IAction | ContextSubMenu> {
|
||||
const actions: Array<IAction | ContextSubMenu> = [];
|
||||
if (breakpoints.length === 1) {
|
||||
const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService));
|
||||
actions.push(new Action(
|
||||
'workbench.debug.action.editBreakpointAction',
|
||||
nls.localize('editBreakpoint', "Edit {0}...", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(breakpoints[0].lineNumber, breakpoints[0].column))
|
||||
));
|
||||
|
||||
actions.push(new Action(
|
||||
`workbench.debug.viewlet.action.toggleBreakpoint`,
|
||||
breakpoints[0].enabled ? nls.localize('disableBreakpoint', "Disable {0}", breakpointType) : nls.localize('enableBreakpoint', "Enable {0}", breakpointType),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!breakpoints[0].enabled, breakpoints[0])
|
||||
));
|
||||
} else if (breakpoints.length > 1) {
|
||||
const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1);
|
||||
actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action(
|
||||
'removeInlineBreakpoint',
|
||||
bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.removeBreakpoints(bp.getId())
|
||||
))));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp =>
|
||||
new Action('editBreakpoint',
|
||||
bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(bp.lineNumber, bp.column))
|
||||
)
|
||||
)));
|
||||
|
||||
actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action(
|
||||
bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint',
|
||||
bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint"))
|
||||
: (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp)
|
||||
))));
|
||||
} else {
|
||||
actions.push(new Action(
|
||||
'addBreakpoint',
|
||||
nls.localize('addBreakpoint', "Add Breakpoint"),
|
||||
undefined,
|
||||
true,
|
||||
() => this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorContextMenu`)
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addConditionalBreakpoint',
|
||||
nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, undefined))
|
||||
));
|
||||
actions.push(new Action(
|
||||
'addLogPoint',
|
||||
nls.localize('addLogPoint', "Add Logpoint..."),
|
||||
undefined,
|
||||
true,
|
||||
() => Promise.resolve(this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, undefined, BreakpointWidgetContext.LOG_MESSAGE))
|
||||
));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => {
|
||||
const data = e.target.detail as IMarginData;
|
||||
const model = this.editor.getModel();
|
||||
if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model);
|
||||
const lineNumber = e.target.position.lineNumber;
|
||||
const uri = model.uri;
|
||||
|
||||
if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) {
|
||||
if (!canSetBreakpoints) {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = { x: e.event.posx, y: e.event.posy };
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber, uri });
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber),
|
||||
getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined
|
||||
});
|
||||
} else {
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber });
|
||||
|
||||
if (breakpoints.length) {
|
||||
// Show the dialog if there is a potential condition to be accidently lost.
|
||||
// Do not show dialog on linux due to electron issue freezing the mouse #50026
|
||||
if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) {
|
||||
const logPoint = breakpoints.every(bp => !!bp.logMessage);
|
||||
const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint");
|
||||
const disable = breakpoints.some(bp => bp.enabled);
|
||||
|
||||
const enabling = nls.localize('breakpointHasConditionDisabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
const disabling = nls.localize('breakpointHasConditionEnabled',
|
||||
"This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.",
|
||||
breakpointType.toLowerCase(),
|
||||
logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition")
|
||||
);
|
||||
|
||||
const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [
|
||||
nls.localize('removeLogPoint', "Remove {0}", breakpointType),
|
||||
nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType),
|
||||
nls.localize('cancel', "Cancel")
|
||||
], { cancelId: 2 });
|
||||
|
||||
if (choice === 0) {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
if (choice === 1) {
|
||||
breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp));
|
||||
}
|
||||
} else {
|
||||
breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId()));
|
||||
}
|
||||
} else if (canSetBreakpoints) {
|
||||
this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
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) &&
|
||||
this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) {
|
||||
const data = e.target.detail as IMarginData;
|
||||
if (!data.isAfterLines) {
|
||||
showBreakpointHintAtLineNumber = e.target.position.lineNumber;
|
||||
}
|
||||
}
|
||||
this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber);
|
||||
}));
|
||||
this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => {
|
||||
this.ensureBreakpointHintDecoration(-1);
|
||||
}));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(e => this.onFocusStackFrame(e.stackFrame)));
|
||||
|
||||
// hover listeners & hover widget
|
||||
@@ -279,7 +104,6 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
if (model) {
|
||||
this._applyHoverConfiguration(model, stackFrame);
|
||||
}
|
||||
this.closeBreakpointWidget();
|
||||
this.toggleExceptionWidget();
|
||||
this.hideHoverWidget();
|
||||
this.updateConfigurationWidgetVisibility();
|
||||
@@ -317,11 +141,11 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
getId(): string {
|
||||
return EDITOR_CONTRIBUTION_ID;
|
||||
}
|
||||
|
||||
public showHover(range: Range, focus: boolean): Promise<void> {
|
||||
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()) {
|
||||
@@ -331,36 +155,6 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private marginFreeFromNonDebugDecorations(line: number): boolean {
|
||||
const decorations = this.editor.getLineDecorations(line);
|
||||
if (decorations) {
|
||||
for (const { options } of decorations) {
|
||||
if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void {
|
||||
const newDecoration: IModelDeltaDecoration[] = [];
|
||||
if (showBreakpointHintAtLineNumber !== -1) {
|
||||
newDecoration.push({
|
||||
options: DebugEditorContribution.BREAKPOINT_HELPER_DECORATION,
|
||||
range: {
|
||||
startLineNumber: showBreakpointHintAtLineNumber,
|
||||
startColumn: 1,
|
||||
endLineNumber: showBreakpointHintAtLineNumber,
|
||||
endColumn: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration);
|
||||
}
|
||||
|
||||
private onFocusStackFrame(sf: IStackFrame | undefined): void {
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
@@ -464,29 +258,8 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
this.hideHoverWidget();
|
||||
}
|
||||
}
|
||||
|
||||
// end hover business
|
||||
|
||||
// breakpoint widget
|
||||
public showBreakpointWidget(lineNumber: number, column: number, context?: BreakpointWidgetContext): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
|
||||
this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, context);
|
||||
this.breakpointWidget.show({ lineNumber, column: 1 }, 2);
|
||||
this.breakpointWidgetVisible.set(true);
|
||||
}
|
||||
|
||||
public closeBreakpointWidget(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
this.breakpointWidget = undefined;
|
||||
this.breakpointWidgetVisible.reset();
|
||||
this.editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// exception widget
|
||||
private toggleExceptionWidget(): void {
|
||||
// Toggles exception widget based on the state of the current editor model and debug stack frame
|
||||
@@ -547,7 +320,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
public addLaunchConfiguration(): Promise<any> {
|
||||
addLaunchConfiguration(): Promise<any> {
|
||||
/* __GDPR__
|
||||
"debug/addLaunchConfiguration" : {}
|
||||
*/
|
||||
@@ -594,11 +367,6 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return insertLine(configurationsArrayPosition).then(() => this.commandService.executeCommand('editor.action.triggerSuggest'));
|
||||
}
|
||||
|
||||
private static BREAKPOINT_HELPER_DECORATION: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-breakpoint-hint',
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
|
||||
};
|
||||
|
||||
// Inline Decorations
|
||||
|
||||
@memoize
|
||||
@@ -768,17 +536,14 @@ export class DebugEditorContribution implements IDebugEditorContribution {
|
||||
return this.wordToLineNumbersMap;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.breakpointWidget) {
|
||||
this.breakpointWidget.dispose();
|
||||
}
|
||||
dispose(): void {
|
||||
if (this.hoverWidget) {
|
||||
this.hoverWidget.dispose();
|
||||
}
|
||||
if (this.configurationWidget) {
|
||||
this.configurationWidget.dispose();
|
||||
}
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as lifecycle from 'vs/base/common/lifecycle';
|
||||
import { Constants } from 'vs/editor/common/core/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';
|
||||
import { IDebugService, IBreakpoint, State, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
interface IBreakpointDecoration {
|
||||
decorationId: string;
|
||||
modelId: string;
|
||||
range: Range;
|
||||
}
|
||||
|
||||
interface IDebugEditorModelData {
|
||||
model: ITextModel;
|
||||
toDispose: lifecycle.IDisposable[];
|
||||
breakpointDecorations: IBreakpointDecoration[];
|
||||
currentStackDecorations: string[];
|
||||
topStackFrameRange: Range | undefined;
|
||||
}
|
||||
|
||||
export class DebugEditorModelManager implements IWorkbenchContribution {
|
||||
static readonly ID = 'breakpointManager';
|
||||
static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
|
||||
private modelDataMap: Map<string, IDebugEditorModelData>;
|
||||
private toDispose: lifecycle.IDisposable[];
|
||||
private ignoreDecorationsChangedEvent = false;
|
||||
|
||||
constructor(
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IDebugService private readonly debugService: IDebugService,
|
||||
) {
|
||||
this.modelDataMap = new Map<string, IDebugEditorModelData>();
|
||||
this.toDispose = [];
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
lifecycle.dispose(modelData.toDispose);
|
||||
modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), []);
|
||||
modelData.model.deltaDecorations(modelData.currentStackDecorations, []);
|
||||
});
|
||||
this.toDispose = lifecycle.dispose(this.toDispose);
|
||||
|
||||
this.modelDataMap.clear();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this));
|
||||
this.modelService.getModels().forEach(model => this.onModelAdded(model));
|
||||
this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this));
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame()));
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
if (state === State.Inactive) {
|
||||
this.modelDataMap.forEach(modelData => {
|
||||
modelData.topStackFrameRange = undefined;
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onModelAdded(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri });
|
||||
|
||||
const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr));
|
||||
const desiredDecorations = this.createBreakpointDecorations(model, breakpoints);
|
||||
const breakpointDecorationIds = model.deltaDecorations([], desiredDecorations);
|
||||
const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUriStr))];
|
||||
|
||||
this.modelDataMap.set(modelUriStr, {
|
||||
model: model,
|
||||
toDispose: toDispose,
|
||||
breakpointDecorations: breakpointDecorationIds.map((decorationId, index) => ({ decorationId, modelId: breakpoints[index].getId(), range: desiredDecorations[index].range })),
|
||||
currentStackDecorations: currentStackDecorations,
|
||||
topStackFrameRange: undefined
|
||||
});
|
||||
}
|
||||
|
||||
private onModelRemoved(model: ITextModel): void {
|
||||
const modelUriStr = model.uri.toString();
|
||||
const data = this.modelDataMap.get(modelUriStr);
|
||||
if (data) {
|
||||
lifecycle.dispose(data.toDispose);
|
||||
this.modelDataMap.delete(modelUriStr);
|
||||
}
|
||||
}
|
||||
|
||||
// call stack management. Represent data coming from the debug service.
|
||||
|
||||
private onFocusStackFrame(): void {
|
||||
this.modelDataMap.forEach((modelData, uri) => {
|
||||
modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri));
|
||||
});
|
||||
}
|
||||
|
||||
private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] {
|
||||
const result: IModelDeltaDecoration[] = [];
|
||||
const stackFrame = this.debugService.getViewModel().focusedStackFrame;
|
||||
if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// only show decorations for the currently focused thread.
|
||||
const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER);
|
||||
const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1);
|
||||
|
||||
// compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame,
|
||||
// an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line).
|
||||
const callStack = stackFrame.thread.getCallStack();
|
||||
if (callStack && callStack.length && stackFrame === callStack[0]) {
|
||||
result.push({
|
||||
options: DebugEditorModelManager.TOP_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugEditorModelManager.TOP_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
|
||||
const modelData = this.modelDataMap.get(modelUriStr);
|
||||
if (modelData) {
|
||||
if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) {
|
||||
result.push({
|
||||
options: DebugEditorModelManager.TOP_STACK_FRAME_INLINE_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
modelData.topStackFrameRange = columnUntilEOLRange;
|
||||
}
|
||||
} else {
|
||||
result.push({
|
||||
options: DebugEditorModelManager.FOCUSED_STACK_FRAME_MARGIN,
|
||||
range
|
||||
});
|
||||
|
||||
result.push({
|
||||
options: DebugEditorModelManager.FOCUSED_STACK_FRAME_DECORATION,
|
||||
range: columnUntilEOLRange
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// breakpoints management. Represent data coming from the debug service and also send data back.
|
||||
private onModelDecorationsChanged(modelUrlStr: string): void {
|
||||
const modelData = this.modelDataMap.get(modelUrlStr);
|
||||
if (!modelData || modelData.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent) {
|
||||
// I have no decorations
|
||||
return;
|
||||
}
|
||||
let somethingChanged = false;
|
||||
modelData.breakpointDecorations.forEach(breakpointDecoration => {
|
||||
if (somethingChanged) {
|
||||
return;
|
||||
}
|
||||
const newBreakpointRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) {
|
||||
somethingChanged = true;
|
||||
}
|
||||
});
|
||||
if (!somethingChanged) {
|
||||
// nothing to do, my decorations did not change.
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new Map<string, IBreakpointUpdateData>();
|
||||
const breakpoints = this.debugService.getModel().getBreakpoints();
|
||||
const modelUri = modelData.model.uri;
|
||||
for (let i = 0, len = modelData.breakpointDecorations.length; i < len; i++) {
|
||||
const breakpointDecoration = modelData.breakpointDecorations[i];
|
||||
const decorationRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId);
|
||||
// check if the line got deleted.
|
||||
if (decorationRange) {
|
||||
const breakpoint = breakpoints.filter(bp => bp.getId() === breakpointDecoration.modelId).pop();
|
||||
// since we know it is collapsed, it cannot grow to multiple lines
|
||||
if (breakpoint) {
|
||||
data.set(breakpoint.getId(), {
|
||||
lineNumber: decorationRange.startLineNumber,
|
||||
column: breakpoint.column ? decorationRange.startColumn : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.debugService.updateBreakpoints(modelUri, data, true).then(undefined, onUnexpectedError);
|
||||
}
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
const breakpointsMap = new Map<string, IBreakpoint[]>();
|
||||
this.debugService.getModel().getBreakpoints().forEach(bp => {
|
||||
const uriStr = bp.uri.toString();
|
||||
const breakpoints = breakpointsMap.get(uriStr);
|
||||
if (breakpoints) {
|
||||
breakpoints.push(bp);
|
||||
} else {
|
||||
breakpointsMap.set(uriStr, [bp]);
|
||||
}
|
||||
});
|
||||
|
||||
breakpointsMap.forEach((bps, uri) => {
|
||||
const data = this.modelDataMap.get(uri);
|
||||
if (data) {
|
||||
this.updateBreakpoints(data, breakpointsMap.get(uri)!);
|
||||
}
|
||||
});
|
||||
this.modelDataMap.forEach((modelData, uri) => {
|
||||
if (!breakpointsMap.has(uri)) {
|
||||
this.updateBreakpoints(modelData, []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void {
|
||||
const desiredDecorations = this.createBreakpointDecorations(modelData.model, newBreakpoints);
|
||||
try {
|
||||
this.ignoreDecorationsChangedEvent = true;
|
||||
const breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), desiredDecorations);
|
||||
modelData.breakpointDecorations = breakpointDecorationIds.map((decorationId, index) => ({
|
||||
decorationId,
|
||||
modelId: newBreakpoints[index].getId(),
|
||||
range: desiredDecorations[index].range
|
||||
}));
|
||||
} finally {
|
||||
this.ignoreDecorationsChangedEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
private createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray<IBreakpoint>): { range: Range; options: IModelDecorationOptions; }[] {
|
||||
const result: { range: Range; options: IModelDecorationOptions; }[] = [];
|
||||
breakpoints.forEach((breakpoint) => {
|
||||
if (breakpoint.lineNumber <= model.getLineCount()) {
|
||||
const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber);
|
||||
const range = model.validateRange(
|
||||
breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1)
|
||||
: new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688
|
||||
);
|
||||
|
||||
result.push({
|
||||
options: this.getBreakpointDecorationOptions(breakpoint),
|
||||
range
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getBreakpointDecorationOptions(breakpoint: IBreakpoint): IModelDecorationOptions {
|
||||
const { className, message } = getBreakpointMessageAndClassName(this.debugService, breakpoint);
|
||||
let glyphMarginHoverMessage: MarkdownString | undefined;
|
||||
|
||||
if (message) {
|
||||
if (breakpoint.condition || breakpoint.hitCondition) {
|
||||
const modelData = this.modelDataMap.get(breakpoint.uri.toString());
|
||||
const modeId = modelData ? modelData.model.getLanguageIdentifier().language : '';
|
||||
glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message);
|
||||
} else {
|
||||
glyphMarginHoverMessage = new MarkdownString().appendText(message);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
glyphMarginClassName: className,
|
||||
glyphMarginHoverMessage,
|
||||
stickiness: DebugEditorModelManager.STICKINESS,
|
||||
beforeContentClassName: breakpoint.column ? `debug-breakpoint-column ${className}-column` : undefined
|
||||
};
|
||||
}
|
||||
|
||||
// editor decorations
|
||||
|
||||
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
|
||||
private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-top-stack-frame',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
|
||||
glyphMarginClassName: 'debug-focused-stack-frame',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-top-stack-frame-line',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
|
||||
private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = {
|
||||
beforeContentClassName: 'debug-top-stack-frame-column'
|
||||
};
|
||||
|
||||
private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'debug-remove-token-colors',
|
||||
className: 'debug-focused-stack-frame-line',
|
||||
stickiness: DebugEditorModelManager.STICKINESS
|
||||
};
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const topStackFrame = theme.getColor(topStackFrameColor);
|
||||
if (topStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`);
|
||||
}
|
||||
|
||||
const focusedStackFrame = theme.getColor(focusedStackFrameColor);
|
||||
if (focusedStackFrame) {
|
||||
collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
|
||||
const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
|
||||
@@ -10,19 +10,20 @@ import * as platform from 'vs/base/common/platform';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { CompletionItem, completionKindFromString } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -34,6 +35,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
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';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
|
||||
export class DebugSession implements IDebugSession {
|
||||
|
||||
@@ -73,7 +75,7 @@ export class DebugSession implements IDebugSession {
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
|
||||
@IOpenerService private readonly openerService: IOpenerService
|
||||
) {
|
||||
this.id = generateUuid();
|
||||
@@ -185,7 +187,7 @@ export class DebugSession implements IDebugSession {
|
||||
|
||||
return dbgr.createDebugAdapter(this).then(debugAdapter => {
|
||||
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService);
|
||||
this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService);
|
||||
|
||||
return this.raw.start().then(() => {
|
||||
|
||||
@@ -284,15 +286,7 @@ export class DebugSession implements IDebugSession {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const source = this.getSourceForUri(modelUri);
|
||||
let rawSource: DebugProtocol.Source;
|
||||
if (source) {
|
||||
rawSource = source.raw;
|
||||
} else {
|
||||
const data = Source.getEncodedDebugData(modelUri);
|
||||
rawSource = { name: data.name, path: data.path, sourceReference: data.sourceReference };
|
||||
}
|
||||
|
||||
const rawSource = this.getRawSource(modelUri);
|
||||
if (breakpointsToSend.length && !rawSource.adapterData) {
|
||||
rawSource.adapterData = breakpointsToSend[0].adapterData;
|
||||
}
|
||||
@@ -376,6 +370,17 @@ export class DebugSession implements IDebugSession {
|
||||
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}`);
|
||||
}
|
||||
return Promise.reject(new Error('no debug adapter'));
|
||||
}
|
||||
|
||||
customRequest(request: string, args: any): Promise<DebugProtocol.Response> {
|
||||
if (this.raw) {
|
||||
return this.raw.custom(request, args);
|
||||
@@ -914,6 +919,16 @@ export class DebugSession implements IDebugSession {
|
||||
return source;
|
||||
}
|
||||
|
||||
private getRawSource(uri: URI): DebugProtocol.Source {
|
||||
const source = this.getSourceForUri(uri);
|
||||
if (source) {
|
||||
return source.raw;
|
||||
} else {
|
||||
const data = Source.getEncodedDebugData(uri);
|
||||
return { name: data.name, path: data.path, sourceReference: data.sourceReference };
|
||||
}
|
||||
}
|
||||
|
||||
private getNewCancellationToken(threadId: number): CancellationToken {
|
||||
const tokenSource = new CancellationTokenSource();
|
||||
const tokens = this.cancellationMap.get(threadId) || [];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
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,8 +13,10 @@ 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';
|
||||
|
||||
class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService {
|
||||
|
||||
constructor(
|
||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||
@@ -45,6 +47,52 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
// we pass the "ParsedArgs" as query parameters of the URL
|
||||
|
||||
let newAddress = `${document.location.origin}${document.location.pathname}?`;
|
||||
let gotFolder = false;
|
||||
|
||||
const addQueryParameter = (key: string, value: string) => {
|
||||
const lastChar = newAddress.charAt(newAddress.length - 1);
|
||||
if (lastChar !== '?' && lastChar !== '&') {
|
||||
newAddress += '&';
|
||||
}
|
||||
newAddress += `${key}=${encodeURIComponent(value)}`;
|
||||
};
|
||||
|
||||
const f = args['folder-uri'];
|
||||
if (f) {
|
||||
const u = URI.parse(f[0]);
|
||||
gotFolder = true;
|
||||
addQueryParameter('folder', u.path);
|
||||
}
|
||||
if (!gotFolder) {
|
||||
// request empty window
|
||||
addQueryParameter('ew', 'true');
|
||||
}
|
||||
|
||||
const ep = args['extensionDevelopmentPath'];
|
||||
if (ep) {
|
||||
let u = ep[0];
|
||||
addQueryParameter('edp', u);
|
||||
}
|
||||
|
||||
const di = args['debugId'];
|
||||
if (di) {
|
||||
addQueryParameter('di', di);
|
||||
}
|
||||
|
||||
const ibe = args['inspect-brk-extensions'];
|
||||
if (ibe) {
|
||||
addQueryParameter('ibe', ibe);
|
||||
}
|
||||
|
||||
window.open(newAddress);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#C5C5C5"/>
|
||||
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#C5C5C5"/>
|
||||
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#C5C5C5"/>
|
||||
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#C5C5C5"/>
|
||||
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 484 B |
@@ -1,7 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="white"/>
|
||||
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="white"/>
|
||||
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="white"/>
|
||||
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="white"/>
|
||||
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 474 B |
@@ -1,7 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#424242"/>
|
||||
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#424242"/>
|
||||
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#424242"/>
|
||||
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#424242"/>
|
||||
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 484 B |
@@ -17,12 +17,20 @@
|
||||
}
|
||||
|
||||
.debug-breakpoint-disabled,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-disabled-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-disabled {
|
||||
background: url('breakpoint-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-disabled:hover {
|
||||
background: url('breakpoint-hint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget.line-start {
|
||||
left: -0.45em !important;
|
||||
}
|
||||
|
||||
.debug-breakpoint-unverified,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unverified-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unverified {
|
||||
background: url('breakpoint-unverified.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -35,21 +43,31 @@
|
||||
}
|
||||
|
||||
.debug-breakpoint,
|
||||
.monaco-editor .debug-breakpoint-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget {
|
||||
background: url('breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-breakpoint-column::before,
|
||||
.monaco-editor .debug-breakpoint-placeholder::before,
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
content: " ";
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame-column::before {
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.monaco-editor .inline-breakpoint-widget {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
margin-left: 0.61em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.debug-function-breakpoint {
|
||||
background: url('breakpoint-function.svg') center center no-repeat;
|
||||
}
|
||||
@@ -75,34 +93,34 @@
|
||||
}
|
||||
|
||||
.debug-breakpoint-conditional,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-conditional-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-conditional {
|
||||
background: url('breakpoint-conditional.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-log,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-log-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log {
|
||||
background: url('breakpoint-log.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-log-disabled,
|
||||
.monaco-editor .debug-breakpoint-log-disabled-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log-disabled {
|
||||
background: url('breakpoint-log-disabled.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-log-unverified,
|
||||
.monaco-editor .debug-breakpoint-log-unverified-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log-unverified {
|
||||
background: url('breakpoint-log-unverified.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.debug-breakpoint-unsupported,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-unsupported-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unsupported {
|
||||
background: url('breakpoint-unsupported.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint,
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint-conditional,
|
||||
.monaco-editor .debug-top-stack-frame.debug-breakpoint-log,
|
||||
.monaco-editor .debug-breakpoint-column.debug-breakpoint-column.debug-top-stack-frame-column::before {
|
||||
.monaco-editor .inline-breakpoint-widget.debug-top-stack-frame-column {
|
||||
background: url('current-and-breakpoint.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,19 +88,6 @@
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.debug-action.clear-repl {
|
||||
background: url('clear-light.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .debug-action.clear-repl {
|
||||
background: url('clear-dark.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.hc-black .debug-action.clear-repl {
|
||||
background: url('clear-hc.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* Output coloring and styling */
|
||||
.repl .repl-tree .output.expression > .ignore {
|
||||
font-style: italic;
|
||||
|
||||
@@ -13,7 +13,7 @@ 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 { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { env as processEnv } from 'vs/base/common/process';
|
||||
@@ -79,7 +79,7 @@ export class RawDebugSession implements IDisposable {
|
||||
dbgr: IDebugger,
|
||||
private readonly telemetryService: ITelemetryService,
|
||||
public readonly customTelemetryService: ITelemetryService | undefined,
|
||||
private readonly windowsService: IWindowsService,
|
||||
private readonly extensionHostDebugService: IExtensionHostDebugService,
|
||||
private readonly openerService: IOpenerService
|
||||
|
||||
) {
|
||||
@@ -381,6 +381,13 @@ export class RawDebugSession implements IDisposable {
|
||||
return this.send<DebugProtocol.SetExceptionBreakpointsResponse>('setExceptionBreakpoints', args);
|
||||
}
|
||||
|
||||
breakpointLocations(args: DebugProtocol.BreakpointLocationsArguments): Promise<DebugProtocol.BreakpointLocationsResponse> {
|
||||
if (this.capabilities.supportsBreakpointLocationsRequest) {
|
||||
return this.send('breakpointLocations', args);
|
||||
}
|
||||
return Promise.reject(new Error('breakpointLocations is not supported'));
|
||||
}
|
||||
|
||||
configurationDone(): Promise<DebugProtocol.ConfigurationDoneResponse> {
|
||||
if (this.capabilities.supportsConfigurationDoneRequest) {
|
||||
return this.send('configurationDone', null);
|
||||
@@ -594,7 +601,7 @@ export class RawDebugSession implements IDisposable {
|
||||
} else {
|
||||
args[key] = [value];
|
||||
}
|
||||
} else if (key === 'extensionDevelopmentPath') {
|
||||
} else if (key === 'extensionDevelopmentPath' || key === 'enable-proposed-api') {
|
||||
const v = args[key];
|
||||
if (v) {
|
||||
v.push(value);
|
||||
@@ -625,7 +632,7 @@ export class RawDebugSession implements IDisposable {
|
||||
Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]);
|
||||
}
|
||||
|
||||
return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
|
||||
return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, env);
|
||||
}
|
||||
|
||||
private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number): Promise<R> {
|
||||
|
||||
@@ -1008,7 +1008,7 @@ export class ClearReplAction extends Action {
|
||||
constructor(id: string, label: string,
|
||||
@IPanelService private readonly panelService: IPanelService
|
||||
) {
|
||||
super(id, label, 'debug-action clear-repl');
|
||||
super(id, label, 'debug-action codicon-clear-all');
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
|
||||
import { IEditor, ITextEditor } from 'vs/workbench/common/editor';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
@@ -58,6 +58,7 @@ export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey<boolean>('resta
|
||||
export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey<boolean>('jumpToCursorSupported', false);
|
||||
|
||||
export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug';
|
||||
export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint';
|
||||
export const DEBUG_SCHEME = 'debug';
|
||||
export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = {
|
||||
enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'],
|
||||
@@ -207,6 +208,7 @@ export interface IDebugSession extends ITreeElement {
|
||||
dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }>;
|
||||
sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise<void>;
|
||||
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void>;
|
||||
breakpointsLocations(uri: uri, lineNumber: number): Promise<IPosition[]>;
|
||||
|
||||
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse>;
|
||||
exceptionInfo(threadId: number): Promise<IExceptionInfo | undefined>;
|
||||
@@ -850,9 +852,12 @@ export const enum BreakpointWidgetContext {
|
||||
|
||||
export interface IDebugEditorContribution extends IEditorContribution {
|
||||
showHover(range: Range, focus: boolean): Promise<void>;
|
||||
addLaunchConfiguration(): Promise<any>;
|
||||
}
|
||||
|
||||
export interface IBreakpointEditorContribution extends IEditorContribution {
|
||||
showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void;
|
||||
closeBreakpointWidget(): void;
|
||||
addLaunchConfiguration(): Promise<any>;
|
||||
}
|
||||
|
||||
// temporary debug helper service
|
||||
|
||||
@@ -7,13 +7,22 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
|
||||
import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
|
||||
export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient {
|
||||
|
||||
constructor(
|
||||
@IMainProcessService readonly windowService: IMainProcessService,
|
||||
@IMainProcessService readonly mainProcessService: IMainProcessService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService
|
||||
) {
|
||||
super(windowService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
|
||||
super(mainProcessService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName));
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> {
|
||||
// TODO@Isidor use debug IPC channel
|
||||
return this.windowsService.openExtensionDevelopmentHostWindow(args, env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
@@ -132,6 +132,11 @@ export class MockDebugService implements IDebugService {
|
||||
}
|
||||
|
||||
export class MockSession implements IDebugSession {
|
||||
|
||||
breakpointsLocations(uri: uri, lineNumber: number): Promise<IPosition[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; }> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user