UNPKG

monaco-editor

Version:
342 lines (341 loc) • 19.1 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var MarkerNavigationWidget_1; import * as dom from '../../../../base/browser/dom.js'; import { ScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js'; import { isNonEmptyArray } from '../../../../base/common/arrays.js'; import { Color } from '../../../../base/common/color.js'; import { Emitter } from '../../../../base/common/event.js'; import { DisposableStore, dispose } from '../../../../base/common/lifecycle.js'; import { basename } from '../../../../base/common/resources.js'; import { splitLines } from '../../../../base/common/strings.js'; import './media/gotoErrorWidget.css'; import { Range } from '../../../common/core/range.js'; import { peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from '../../peekView/browser/peekView.js'; import * as nls from '../../../../nls.js'; import { createAndFillInActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; import { contrastBorder, editorBackground, editorErrorBorder, editorErrorForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, oneOf, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; class MessageWidget { constructor(parent, editor, onRelatedInformation, _openerService, _labelService) { this._openerService = _openerService; this._labelService = _labelService; this._lines = 0; this._longestLineLength = 0; this._relatedDiagnostics = new WeakMap(); this._disposables = new DisposableStore(); this._editor = editor; const domNode = document.createElement('div'); domNode.className = 'descriptioncontainer'; this._messageBlock = document.createElement('div'); this._messageBlock.classList.add('message'); this._messageBlock.setAttribute('aria-live', 'assertive'); this._messageBlock.setAttribute('role', 'alert'); domNode.appendChild(this._messageBlock); this._relatedBlock = document.createElement('div'); domNode.appendChild(this._relatedBlock); this._disposables.add(dom.addStandardDisposableListener(this._relatedBlock, 'click', event => { event.preventDefault(); const related = this._relatedDiagnostics.get(event.target); if (related) { onRelatedInformation(related); } })); this._scrollable = new ScrollableElement(domNode, { horizontal: 1 /* ScrollbarVisibility.Auto */, vertical: 1 /* ScrollbarVisibility.Auto */, useShadows: false, horizontalScrollbarSize: 6, verticalScrollbarSize: 6 }); parent.appendChild(this._scrollable.getDomNode()); this._disposables.add(this._scrollable.onScroll(e => { domNode.style.left = `-${e.scrollLeft}px`; domNode.style.top = `-${e.scrollTop}px`; })); this._disposables.add(this._scrollable); } dispose() { dispose(this._disposables); } update(marker) { const { source, message, relatedInformation, code } = marker; let sourceAndCodeLength = (source?.length || 0) + '()'.length; if (code) { if (typeof code === 'string') { sourceAndCodeLength += code.length; } else { sourceAndCodeLength += code.value.length; } } const lines = splitLines(message); this._lines = lines.length; this._longestLineLength = 0; for (const line of lines) { this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength); } dom.clearNode(this._messageBlock); this._messageBlock.setAttribute('aria-label', this.getAriaLabel(marker)); this._editor.applyFontInfo(this._messageBlock); let lastLineElement = this._messageBlock; for (const line of lines) { lastLineElement = document.createElement('div'); lastLineElement.innerText = line; if (line === '') { lastLineElement.style.height = this._messageBlock.style.lineHeight; } this._messageBlock.appendChild(lastLineElement); } if (source || code) { const detailsElement = document.createElement('span'); detailsElement.classList.add('details'); lastLineElement.appendChild(detailsElement); if (source) { const sourceElement = document.createElement('span'); sourceElement.innerText = source; sourceElement.classList.add('source'); detailsElement.appendChild(sourceElement); } if (code) { if (typeof code === 'string') { const codeElement = document.createElement('span'); codeElement.innerText = `(${code})`; codeElement.classList.add('code'); detailsElement.appendChild(codeElement); } else { this._codeLink = dom.$('a.code-link'); this._codeLink.setAttribute('href', `${code.target.toString()}`); this._codeLink.onclick = (e) => { this._openerService.open(code.target, { allowCommands: true }); e.preventDefault(); e.stopPropagation(); }; const codeElement = dom.append(this._codeLink, dom.$('span')); codeElement.innerText = code.value; detailsElement.appendChild(this._codeLink); } } } dom.clearNode(this._relatedBlock); this._editor.applyFontInfo(this._relatedBlock); if (isNonEmptyArray(relatedInformation)) { const relatedInformationNode = this._relatedBlock.appendChild(document.createElement('div')); relatedInformationNode.style.paddingTop = `${Math.floor(this._editor.getOption(67 /* EditorOption.lineHeight */) * 0.66)}px`; this._lines += 1; for (const related of relatedInformation) { const container = document.createElement('div'); const relatedResource = document.createElement('a'); relatedResource.classList.add('filename'); relatedResource.innerText = `${this._labelService.getUriBasenameLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `; relatedResource.title = this._labelService.getUriLabel(related.resource); this._relatedDiagnostics.set(relatedResource, related); const relatedMessage = document.createElement('span'); relatedMessage.innerText = related.message; container.appendChild(relatedResource); container.appendChild(relatedMessage); this._lines += 1; relatedInformationNode.appendChild(container); } } const fontInfo = this._editor.getOption(50 /* EditorOption.fontInfo */); const scrollWidth = Math.ceil(fontInfo.typicalFullwidthCharacterWidth * this._longestLineLength * 0.75); const scrollHeight = fontInfo.lineHeight * this._lines; this._scrollable.setScrollDimensions({ scrollWidth, scrollHeight }); } layout(height, width) { this._scrollable.getDomNode().style.height = `${height}px`; this._scrollable.getDomNode().style.width = `${width}px`; this._scrollable.setScrollDimensions({ width, height }); } getHeightInLines() { return Math.min(17, this._lines); } getAriaLabel(marker) { let severityLabel = ''; switch (marker.severity) { case MarkerSeverity.Error: severityLabel = nls.localize('Error', "Error"); break; case MarkerSeverity.Warning: severityLabel = nls.localize('Warning', "Warning"); break; case MarkerSeverity.Info: severityLabel = nls.localize('Info', "Info"); break; case MarkerSeverity.Hint: severityLabel = nls.localize('Hint', "Hint"); break; } let ariaLabel = nls.localize('marker aria', "{0} at {1}. ", severityLabel, marker.startLineNumber + ':' + marker.startColumn); const model = this._editor.getModel(); if (model && (marker.startLineNumber <= model.getLineCount()) && (marker.startLineNumber >= 1)) { const lineContent = model.getLineContent(marker.startLineNumber); ariaLabel = `${lineContent}, ${ariaLabel}`; } return ariaLabel; } } let MarkerNavigationWidget = class MarkerNavigationWidget extends PeekViewWidget { static { MarkerNavigationWidget_1 = this; } static { this.TitleMenu = new MenuId('gotoErrorTitleMenu'); } constructor(editor, _themeService, _openerService, _menuService, instantiationService, _contextKeyService, _labelService) { super(editor, { showArrow: true, showFrame: true, isAccessible: true, frameWidth: 1 }, instantiationService); this._themeService = _themeService; this._openerService = _openerService; this._menuService = _menuService; this._contextKeyService = _contextKeyService; this._labelService = _labelService; this._callOnDispose = new DisposableStore(); this._onDidSelectRelatedInformation = new Emitter(); this.onDidSelectRelatedInformation = this._onDidSelectRelatedInformation.event; this._severity = MarkerSeverity.Warning; this._backgroundColor = Color.white; this._applyTheme(_themeService.getColorTheme()); this._callOnDispose.add(_themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this.create(); } _applyTheme(theme) { this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); let colorId = editorMarkerNavigationError; let headerBackground = editorMarkerNavigationErrorHeader; if (this._severity === MarkerSeverity.Warning) { colorId = editorMarkerNavigationWarning; headerBackground = editorMarkerNavigationWarningHeader; } else if (this._severity === MarkerSeverity.Info) { colorId = editorMarkerNavigationInfo; headerBackground = editorMarkerNavigationInfoHeader; } const frameColor = theme.getColor(colorId); const headerBg = theme.getColor(headerBackground); this.style({ arrowColor: frameColor, frameColor: frameColor, headerBackgroundColor: headerBg, primaryHeadingColor: theme.getColor(peekViewTitleForeground), secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) }); // style() will trigger _applyStyles } _applyStyles() { if (this._parentContainer) { this._parentContainer.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : ''; } super._applyStyles(); } dispose() { this._callOnDispose.dispose(); super.dispose(); } _fillHead(container) { super._fillHead(container); this._disposables.add(this._actionbarWidget.actionRunner.onWillRun(e => this.editor.focus())); const actions = []; const menu = this._menuService.getMenuActions(MarkerNavigationWidget_1.TitleMenu, this._contextKeyService); createAndFillInActionBarActions(menu, actions); this._actionbarWidget.push(actions, { label: false, icon: true, index: 0 }); } _fillTitleIcon(container) { this._icon = dom.append(container, dom.$('')); } _fillBody(container) { this._parentContainer = container; container.classList.add('marker-widget'); this._parentContainer.tabIndex = 0; this._parentContainer.setAttribute('role', 'tooltip'); this._container = document.createElement('div'); container.appendChild(this._container); this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService, this._labelService); this._disposables.add(this._message); } show() { throw new Error('call showAtMarker'); } showAtMarker(marker, markerIdx, markerCount) { // update: // * title // * message this._container.classList.remove('stale'); this._message.update(marker); // update frame color (only applied on 'show') this._severity = marker.severity; this._applyTheme(this._themeService.getColorTheme()); // show const range = Range.lift(marker); const editorPosition = this.editor.getPosition(); const position = editorPosition && range.containsPosition(editorPosition) ? editorPosition : range.getStartPosition(); super.show(position, this.computeRequiredHeight()); const model = this.editor.getModel(); if (model) { const detail = markerCount > 1 ? nls.localize('problems', "{0} of {1} problems", markerIdx, markerCount) : nls.localize('change', "{0} of {1} problem", markerIdx, markerCount); this.setTitle(basename(model.uri), detail); } this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`; this.editor.revealPositionNearTop(position, 0 /* ScrollType.Smooth */); this.editor.focus(); } updateMarker(marker) { this._container.classList.remove('stale'); this._message.update(marker); } showStale() { this._container.classList.add('stale'); this._relayout(); } _doLayoutBody(heightInPixel, widthInPixel) { super._doLayoutBody(heightInPixel, widthInPixel); this._heightInPixel = heightInPixel; this._message.layout(heightInPixel, widthInPixel); this._container.style.height = `${heightInPixel}px`; } _onWidth(widthInPixel) { this._message.layout(this._heightInPixel, widthInPixel); } _relayout() { super._relayout(this.computeRequiredHeight()); } computeRequiredHeight() { return 3 + this._message.getHeightInLines(); } }; MarkerNavigationWidget = MarkerNavigationWidget_1 = __decorate([ __param(1, IThemeService), __param(2, IOpenerService), __param(3, IMenuService), __param(4, IInstantiationService), __param(5, IContextKeyService), __param(6, ILabelService) ], MarkerNavigationWidget); export { MarkerNavigationWidget }; // theming const errorDefault = oneOf(editorErrorForeground, editorErrorBorder); const warningDefault = oneOf(editorWarningForeground, editorWarningBorder); const infoDefault = oneOf(editorInfoForeground, editorInfoBorder); const editorMarkerNavigationError = registerColor('editorMarkerNavigationError.background', { dark: errorDefault, light: errorDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationError', 'Editor marker navigation widget error color.')); const editorMarkerNavigationErrorHeader = registerColor('editorMarkerNavigationError.headerBackground', { dark: transparent(editorMarkerNavigationError, .1), light: transparent(editorMarkerNavigationError, .1), hcDark: null, hcLight: null }, nls.localize('editorMarkerNavigationErrorHeaderBackground', 'Editor marker navigation widget error heading background.')); const editorMarkerNavigationWarning = registerColor('editorMarkerNavigationWarning.background', { dark: warningDefault, light: warningDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationWarning', 'Editor marker navigation widget warning color.')); const editorMarkerNavigationWarningHeader = registerColor('editorMarkerNavigationWarning.headerBackground', { dark: transparent(editorMarkerNavigationWarning, .1), light: transparent(editorMarkerNavigationWarning, .1), hcDark: '#0C141F', hcLight: transparent(editorMarkerNavigationWarning, .2) }, nls.localize('editorMarkerNavigationWarningBackground', 'Editor marker navigation widget warning heading background.')); const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationInfo.background', { dark: infoDefault, light: infoDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationInfo', 'Editor marker navigation widget info color.')); const editorMarkerNavigationInfoHeader = registerColor('editorMarkerNavigationInfo.headerBackground', { dark: transparent(editorMarkerNavigationInfo, .1), light: transparent(editorMarkerNavigationInfo, .1), hcDark: null, hcLight: null }, nls.localize('editorMarkerNavigationInfoHeaderBackground', 'Editor marker navigation widget info heading background.')); const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', editorBackground, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));