Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { distinct } from '../../../../base/common/arrays.js';
7
import { Event } from '../../../../base/common/event.js';
8
import { Disposable } from '../../../../base/common/lifecycle.js';
9
import { Constants } from '../../../../base/common/uint.js';
10
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
11
import { Range } from '../../../../editor/common/core/range.js';
12
import { IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js';
13
import { GlyphMarginLane, IModelDecorationOptions, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/model.js';
14
import { localize } from '../../../../nls.js';
15
import { ILogService } from '../../../../platform/log/common/log.js';
16
import { registerColor } from '../../../../platform/theme/common/colorRegistry.js';
17
import { themeColorFromId } from '../../../../platform/theme/common/themeService.js';
18
import { ThemeIcon } from '../../../../base/common/themables.js';
19
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
20
import { debugStackframe, debugStackframeFocused } from './debugIcons.js';
21
import { IDebugService, IStackFrame } from '../common/debug.js';
22
import './media/callStackEditorContribution.css';
23
24
export const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hcDark: '#ffff0033', hcLight: '#ffff6673' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.'));
25
export const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hcDark: '#7abd7a4d', hcLight: '#cee7ce73' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.'));
26
const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
27
28
// we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement.
29
const TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = {
30
description: 'top-stack-frame-margin',
31
glyphMarginClassName: ThemeIcon.asClassName(debugStackframe),
32
glyphMargin: { position: GlyphMarginLane.Right },
33
zIndex: 9999,
34
stickiness,
35
overviewRuler: {
36
position: OverviewRulerLane.Full,
37
color: themeColorFromId(topStackFrameColor)
38
}
39
};
40
const FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = {
41
description: 'focused-stack-frame-margin',
42
glyphMarginClassName: ThemeIcon.asClassName(debugStackframeFocused),
43
glyphMargin: { position: GlyphMarginLane.Right },
44
zIndex: 9999,
45
stickiness,
46
overviewRuler: {
47
position: OverviewRulerLane.Full,
48
color: themeColorFromId(focusedStackFrameColor)
49
}
50
};
51
export const TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
52
description: 'top-stack-frame-decoration',
53
isWholeLine: true,
54
className: 'debug-top-stack-frame-line',
55
stickiness
56
};
57
export const FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
58
description: 'focused-stack-frame-decoration',
59
isWholeLine: true,
60
className: 'debug-focused-stack-frame-line',
61
stickiness
62
};
63
64
export const makeStackFrameColumnDecoration = (noCharactersBefore: boolean): IModelDecorationOptions => ({
65
description: 'top-stack-frame-inline-decoration',
66
before: {
67
content: '\uEB8B',
68
inlineClassName: noCharactersBefore ? 'debug-top-stack-frame-column start-of-line' : 'debug-top-stack-frame-column',
69
inlineClassNameAffectsLetterSpacing: true
70
},
71
});
72
73
export function createDecorationsForStackFrame(stackFrame: IStackFrame, isFocusedSession: boolean, noCharactersBefore: boolean): IModelDeltaDecoration[] {
74
// only show decorations for the currently focused thread.
75
const result: IModelDeltaDecoration[] = [];
76
const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER);
77
const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1);
78
79
// compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame,
80
// an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line).
81
const topStackFrame = stackFrame.thread.getTopStackFrame();
82
if (stackFrame.getId() === topStackFrame?.getId()) {
83
if (isFocusedSession) {
84
result.push({
85
options: TOP_STACK_FRAME_MARGIN,
86
range
87
});
88
}
89
90
result.push({
91
options: TOP_STACK_FRAME_DECORATION,
92
range: columnUntilEOLRange
93
});
94
95
if (stackFrame.range.startColumn > 1) {
96
result.push({
97
options: makeStackFrameColumnDecoration(noCharactersBefore),
98
range: columnUntilEOLRange
99
});
100
}
101
} else {
102
if (isFocusedSession) {
103
result.push({
104
options: FOCUSED_STACK_FRAME_MARGIN,
105
range
106
});
107
}
108
109
result.push({
110
options: FOCUSED_STACK_FRAME_DECORATION,
111
range: columnUntilEOLRange
112
});
113
}
114
115
return result;
116
}
117
118
export class CallStackEditorContribution extends Disposable implements IEditorContribution {
119
private decorations: IEditorDecorationsCollection;
120
121
constructor(
122
private readonly editor: ICodeEditor,
123
@IDebugService private readonly debugService: IDebugService,
124
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
125
@ILogService private readonly logService: ILogService,
126
) {
127
super();
128
this.decorations = this.editor.createDecorationsCollection();
129
130
const setDecorations = () => this.decorations.set(this.createCallStackDecorations());
131
this._register(Event.any(this.debugService.getViewModel().onDidFocusStackFrame, this.debugService.getModel().onDidChangeCallStack)(() => {
132
setDecorations();
133
}));
134
this._register(this.editor.onDidChangeModel(e => {
135
if (e.newModelUrl) {
136
setDecorations();
137
}
138
}));
139
setDecorations();
140
}
141
142
private createCallStackDecorations(): IModelDeltaDecoration[] {
143
const editor = this.editor;
144
if (!editor.hasModel()) {
145
return [];
146
}
147
148
const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame;
149
const decorations: IModelDeltaDecoration[] = [];
150
this.debugService.getModel().getSessions().forEach(s => {
151
const isSessionFocused = s === focusedStackFrame?.thread.session;
152
s.getAllThreads().forEach(t => {
153
if (t.stopped) {
154
const callStack = t.getCallStack();
155
const stackFrames: IStackFrame[] = [];
156
if (callStack.length > 0) {
157
// Always decorate top stack frame, and decorate focused stack frame if it is not the top stack frame
158
if (focusedStackFrame && !focusedStackFrame.equals(callStack[0])) {
159
stackFrames.push(focusedStackFrame);
160
}
161
stackFrames.push(callStack[0]);
162
}
163
164
stackFrames.forEach(candidateStackFrame => {
165
if (candidateStackFrame && this.uriIdentityService.extUri.isEqual(candidateStackFrame.source.uri, editor.getModel()?.uri)) {
166
if (candidateStackFrame.range.startLineNumber > editor.getModel()?.getLineCount() || candidateStackFrame.range.startLineNumber < 1) {
167
this.logService.warn(`CallStackEditorContribution: invalid stack frame line number: ${candidateStackFrame.range.startLineNumber}`);
168
return;
169
}
170
171
const noCharactersBefore = editor.getModel().getLineFirstNonWhitespaceColumn(candidateStackFrame.range.startLineNumber) >= candidateStackFrame.range.startColumn;
172
decorations.push(...createDecorationsForStackFrame(candidateStackFrame, isSessionFocused, noCharactersBefore));
173
}
174
});
175
}
176
});
177
});
178
179
// Deduplicate same decorations so colors do not stack #109045
180
return distinct(decorations, d => `${d.options.className} ${d.options.glyphMarginClassName} ${d.range.startLineNumber} ${d.range.startColumn}`);
181
}
182
183
override dispose(): void {
184
super.dispose();
185
this.decorations.clear();
186
}
187
}
188
189
190