Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/exceptionWidget.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 './media/exceptionWidget.css';
7
import * as nls from '../../../../nls.js';
8
import * as dom from '../../../../base/browser/dom.js';
9
import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js';
10
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
11
import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from '../common/debug.js';
12
import { RunOnceScheduler } from '../../../../base/common/async.js';
13
import { IThemeService, IColorTheme } from '../../../../platform/theme/common/themeService.js';
14
import { ThemeIcon } from '../../../../base/common/themables.js';
15
import { Color } from '../../../../base/common/color.js';
16
import { registerColor } from '../../../../platform/theme/common/colorRegistry.js';
17
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
18
import { DebugLinkHoverBehavior, LinkDetector } from './linkDetector.js';
19
import { EditorOption } from '../../../../editor/common/config/editorOptions.js';
20
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
21
import { Action } from '../../../../base/common/actions.js';
22
import { widgetClose } from '../../../../platform/theme/common/iconRegistry.js';
23
const $ = dom.$;
24
25
// theming
26
27
const debugExceptionWidgetBorder = registerColor('debugExceptionWidget.border', '#a31515', nls.localize('debugExceptionWidgetBorder', 'Exception widget border color.'));
28
const debugExceptionWidgetBackground = registerColor('debugExceptionWidget.background', { dark: '#420b0d', light: '#f1dfde', hcDark: '#420b0d', hcLight: '#f1dfde' }, nls.localize('debugExceptionWidgetBackground', 'Exception widget background color.'));
29
30
export class ExceptionWidget extends ZoneWidget {
31
32
private backgroundColor: Color | undefined;
33
34
constructor(
35
editor: ICodeEditor,
36
private exceptionInfo: IExceptionInfo,
37
private debugSession: IDebugSession | undefined,
38
@IThemeService themeService: IThemeService,
39
@IInstantiationService private readonly instantiationService: IInstantiationService
40
) {
41
super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' });
42
43
this.applyTheme(themeService.getColorTheme());
44
this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this)));
45
46
this.create();
47
const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50);
48
this._disposables.add(this.editor.onDidLayoutChange(() => onDidLayoutChangeScheduler.schedule()));
49
this._disposables.add(onDidLayoutChangeScheduler);
50
}
51
52
private applyTheme(theme: IColorTheme): void {
53
this.backgroundColor = theme.getColor(debugExceptionWidgetBackground);
54
const frameColor = theme.getColor(debugExceptionWidgetBorder);
55
this.style({
56
arrowColor: frameColor,
57
frameColor: frameColor
58
}); // style() will trigger _applyStyles
59
}
60
61
protected override _applyStyles(): void {
62
if (this.container) {
63
this.container.style.backgroundColor = this.backgroundColor ? this.backgroundColor.toString() : '';
64
}
65
super._applyStyles();
66
}
67
68
protected _fillContainer(container: HTMLElement): void {
69
this.setCssClass('exception-widget');
70
// Set the font size and line height to the one from the editor configuration.
71
const fontInfo = this.editor.getOption(EditorOption.fontInfo);
72
container.style.fontSize = `${fontInfo.fontSize}px`;
73
container.style.lineHeight = `${fontInfo.lineHeight}px`;
74
container.tabIndex = 0;
75
const title = $('.title');
76
const label = $('.label');
77
dom.append(title, label);
78
const actions = $('.actions');
79
dom.append(title, actions);
80
label.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.');
81
let ariaLabel = label.textContent;
82
83
const actionBar = new ActionBar(actions);
84
actionBar.push(new Action('editor.closeExceptionWidget', nls.localize('close', "Close"), ThemeIcon.asClassName(widgetClose), true, async () => {
85
const contribution = this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);
86
contribution?.closeExceptionWidget();
87
}), { label: false, icon: true });
88
89
dom.append(container, title);
90
91
if (this.exceptionInfo.description) {
92
const description = $('.description');
93
description.textContent = this.exceptionInfo.description;
94
ariaLabel += ', ' + this.exceptionInfo.description;
95
dom.append(container, description);
96
}
97
98
if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) {
99
const stackTrace = $('.stack-trace');
100
const linkDetector = this.instantiationService.createInstance(LinkDetector);
101
const linkedStackTrace = linkDetector.linkify(this.exceptionInfo.details.stackTrace, true, this.debugSession ? this.debugSession.root : undefined, undefined, { type: DebugLinkHoverBehavior.Rich, store: this._disposables });
102
stackTrace.appendChild(linkedStackTrace);
103
dom.append(container, stackTrace);
104
ariaLabel += ', ' + this.exceptionInfo.details.stackTrace;
105
}
106
container.setAttribute('aria-label', ariaLabel);
107
}
108
109
protected override _doLayout(_heightInPixel: number | undefined, _widthInPixel: number | undefined): void {
110
// Reload the height with respect to the exception text content and relayout it to match the line count.
111
this.container!.style.height = 'initial';
112
113
const lineHeight = this.editor.getOption(EditorOption.lineHeight);
114
const arrowHeight = Math.round(lineHeight / 3);
115
const computedLinesNumber = Math.ceil((this.container!.offsetHeight + arrowHeight) / lineHeight);
116
117
this._relayout(computedLinesNumber);
118
}
119
120
focus(): void {
121
// Focus into the container for accessibility purposes so the exception and stack trace gets read
122
this.container?.focus();
123
}
124
125
override hasFocus(): boolean {
126
if (!this.container) {
127
return false;
128
}
129
130
return dom.isAncestorOfActiveElement(this.container);
131
}
132
}
133
134