Path: blob/main/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import './media/exceptionWidget.css';6import * as nls from '../../../../nls.js';7import * as dom from '../../../../base/browser/dom.js';8import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js';9import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';10import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from '../common/debug.js';11import { RunOnceScheduler } from '../../../../base/common/async.js';12import { IThemeService, IColorTheme } from '../../../../platform/theme/common/themeService.js';13import { ThemeIcon } from '../../../../base/common/themables.js';14import { Color } from '../../../../base/common/color.js';15import { registerColor } from '../../../../platform/theme/common/colorRegistry.js';16import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';17import { DebugLinkHoverBehavior, LinkDetector } from './linkDetector.js';18import { EditorOption } from '../../../../editor/common/config/editorOptions.js';19import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';20import { Action } from '../../../../base/common/actions.js';21import { widgetClose } from '../../../../platform/theme/common/iconRegistry.js';22const $ = dom.$;2324// theming2526const debugExceptionWidgetBorder = registerColor('debugExceptionWidget.border', '#a31515', nls.localize('debugExceptionWidgetBorder', 'Exception widget border color.'));27const debugExceptionWidgetBackground = registerColor('debugExceptionWidget.background', { dark: '#420b0d', light: '#f1dfde', hcDark: '#420b0d', hcLight: '#f1dfde' }, nls.localize('debugExceptionWidgetBackground', 'Exception widget background color.'));2829export class ExceptionWidget extends ZoneWidget {3031private backgroundColor: Color | undefined;3233constructor(34editor: ICodeEditor,35private exceptionInfo: IExceptionInfo,36private debugSession: IDebugSession | undefined,37@IThemeService themeService: IThemeService,38@IInstantiationService private readonly instantiationService: IInstantiationService39) {40super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' });4142this.applyTheme(themeService.getColorTheme());43this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this)));4445this.create();46const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50);47this._disposables.add(this.editor.onDidLayoutChange(() => onDidLayoutChangeScheduler.schedule()));48this._disposables.add(onDidLayoutChangeScheduler);49}5051private applyTheme(theme: IColorTheme): void {52this.backgroundColor = theme.getColor(debugExceptionWidgetBackground);53const frameColor = theme.getColor(debugExceptionWidgetBorder);54this.style({55arrowColor: frameColor,56frameColor: frameColor57}); // style() will trigger _applyStyles58}5960protected override _applyStyles(): void {61if (this.container) {62this.container.style.backgroundColor = this.backgroundColor ? this.backgroundColor.toString() : '';63}64super._applyStyles();65}6667protected _fillContainer(container: HTMLElement): void {68this.setCssClass('exception-widget');69// Set the font size and line height to the one from the editor configuration.70const fontInfo = this.editor.getOption(EditorOption.fontInfo);71container.style.fontSize = `${fontInfo.fontSize}px`;72container.style.lineHeight = `${fontInfo.lineHeight}px`;73container.tabIndex = 0;74const title = $('.title');75const label = $('.label');76dom.append(title, label);77const actions = $('.actions');78dom.append(title, actions);79label.textContent = this.exceptionInfo.id ? nls.localize('exceptionThrownWithId', 'Exception has occurred: {0}', this.exceptionInfo.id) : nls.localize('exceptionThrown', 'Exception has occurred.');80let ariaLabel = label.textContent;8182const actionBar = new ActionBar(actions);83actionBar.push(new Action('editor.closeExceptionWidget', nls.localize('close', "Close"), ThemeIcon.asClassName(widgetClose), true, async () => {84const contribution = this.editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);85contribution?.closeExceptionWidget();86}), { label: false, icon: true });8788dom.append(container, title);8990if (this.exceptionInfo.description) {91const description = $('.description');92description.textContent = this.exceptionInfo.description;93ariaLabel += ', ' + this.exceptionInfo.description;94dom.append(container, description);95}9697if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) {98const stackTrace = $('.stack-trace');99const linkDetector = this.instantiationService.createInstance(LinkDetector);100const linkedStackTrace = linkDetector.linkify(this.exceptionInfo.details.stackTrace, true, this.debugSession ? this.debugSession.root : undefined, undefined, { type: DebugLinkHoverBehavior.Rich, store: this._disposables });101stackTrace.appendChild(linkedStackTrace);102dom.append(container, stackTrace);103ariaLabel += ', ' + this.exceptionInfo.details.stackTrace;104}105container.setAttribute('aria-label', ariaLabel);106}107108protected override _doLayout(_heightInPixel: number | undefined, _widthInPixel: number | undefined): void {109// Reload the height with respect to the exception text content and relayout it to match the line count.110this.container!.style.height = 'initial';111112const lineHeight = this.editor.getOption(EditorOption.lineHeight);113const arrowHeight = Math.round(lineHeight / 3);114const computedLinesNumber = Math.ceil((this.container!.offsetHeight + arrowHeight) / lineHeight);115116this._relayout(computedLinesNumber);117}118119focus(): void {120// Focus into the container for accessibility purposes so the exception and stack trace gets read121this.container?.focus();122}123124override hasFocus(): boolean {125if (!this.container) {126return false;127}128129return dom.isAncestorOfActiveElement(this.container);130}131}132133134