From 5bdd5022b2a15c726e5e011b2ebd751637c0feab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 28 Jul 2023 19:33:38 +0200 Subject: [PATCH] some cleanup, use faster str join for classes --- src/browser/renderer/dom/DomRenderer.ts | 37 ++++----- .../dom/DomRendererRowFactory.test.ts | 1 - .../renderer/dom/DomRendererRowFactory.ts | 78 ++++++++++--------- 3 files changed, 56 insertions(+), 60 deletions(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 31d75fcc..e2e0bfb8 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { BOLD_CLASS, CURSOR_BLINK_CLASS, CURSOR_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DIM_CLASS, DomRendererRowFactory, ITALIC_CLASS } from 'browser/renderer/dom/DomRendererRowFactory'; +import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory'; import { SpacingCache } from 'browser/renderer/dom/SpacingCache'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; @@ -25,13 +25,6 @@ const SELECTION_CLASS = 'xterm-selection'; let nextTerminalId = 1; -// font metrics calc settings -export const enum FontMetrics { - START = 32, // start codepoint - MAX = 256, // only calc up to this codepoint (256 means only Basic Latin + Latin-1 Supplement) - BATCH_SIZE = 30, // amount of codepoints to calc in a single batch (sync & blocking) - THRESHOLD = 0.005 // allowed relative deviation from cell width -} /** * A fallback renderer for when canvas is slow. This is not meant to be @@ -153,13 +146,13 @@ export class DomRenderer extends Disposable implements IRenderer { `}`; // Text styles styles += - `${this._terminalSelector} span:not(.${BOLD_CLASS}) {` + + `${this._terminalSelector} span:not(.${RowCss.BOLD_CLASS}) {` + ` font-weight: ${this._optionsService.rawOptions.fontWeight};` + `}` + - `${this._terminalSelector} span.${BOLD_CLASS} {` + + `${this._terminalSelector} span.${RowCss.BOLD_CLASS} {` + ` font-weight: ${this._optionsService.rawOptions.fontWeightBold};` + `}` + - `${this._terminalSelector} span.${ITALIC_CLASS} {` + + `${this._terminalSelector} span.${RowCss.ITALIC_CLASS} {` + ` font-style: italic;` + `}`; // Blink animation @@ -176,33 +169,33 @@ export class DomRenderer extends Disposable implements IRenderer { ` color: ${colors.cursorAccent.css};` + ` }` + ` 50% {` + - ` background-color: ${colors.cursorAccent.css};` + + ` background-color: inherit;` + ` color: ${colors.cursor.css};` + ` }` + `}`; // Cursor styles += - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} ,` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} ,` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} ` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} ,` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} ,` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} ` + `{` + ` outline: 1px solid ${colors.cursor.css};` + ` outline-offset: -1px;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}:not(.${RowCss.CURSOR_STYLE_BLOCK_CLASS}) {` + ` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${colors.cursor.css};` + ` color: ${colors.cursorAccent.css};` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` + ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` + ` box-shadow: 0 -1px 0 ${colors.cursor.css} inset;` + `}`; // Selection @@ -226,12 +219,12 @@ export class DomRenderer extends Disposable implements IRenderer { for (const [i, c] of colors.ansi.entries()) { styles += `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + - `${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` + + `${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; } styles += `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` + - `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` + + `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`; this._themeStyleElement.textContent = styles; diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 64f4b198..1388a0f3 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -12,7 +12,6 @@ import { IBufferLine } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { MockCoreService, MockDecorationService, MockOptionsService } from 'common/TestUtils.test'; import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test'; -import { FontMetrics } from 'browser/renderer/dom/DomRenderer'; import { FontVariant, SpacingCache } from 'browser/renderer/dom/SpacingCache'; class EmptySpacingCache extends SpacingCache { diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index abe48e20..886fc555 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -15,17 +15,21 @@ import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/Rendere import { AttributeData } from 'common/buffer/AttributeData'; import { SpacingCache } from 'browser/renderer/dom/SpacingCache'; -export const BOLD_CLASS = 'xterm-bold'; -export const DIM_CLASS = 'xterm-dim'; -export const ITALIC_CLASS = 'xterm-italic'; -export const UNDERLINE_CLASS = 'xterm-underline'; -export const OVERLINE_CLASS = 'xterm-overline'; -export const STRIKETHROUGH_CLASS = 'xterm-strikethrough'; -export const CURSOR_CLASS = 'xterm-cursor'; -export const CURSOR_BLINK_CLASS = 'xterm-cursor-blink'; -export const CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block'; -export const CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar'; -export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; + +export const enum RowCss { + BOLD_CLASS = 'xterm-bold', + DIM_CLASS = 'xterm-dim', + ITALIC_CLASS = 'xterm-italic', + UNDERLINE_CLASS = 'xterm-underline', + OVERLINE_CLASS = 'xterm-overline', + STRIKETHROUGH_CLASS = 'xterm-strikethrough', + CURSOR_CLASS = 'xterm-cursor', + CURSOR_BLINK_CLASS = 'xterm-cursor-blink', + CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block', + CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar', + CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline' +} + export class DomRendererRowFactory { private _workCell: CellData = new CellData(); @@ -81,6 +85,7 @@ export class DomRendererRowFactory { let oldLinkHover: number | boolean = false; let oldSpacing = 0; let spacing = 0; + const classes: string[] = []; const hasHover = linkStart !== -1 && linkEnd !== -1; @@ -179,35 +184,29 @@ export class DomRendererRowFactory { } if (!this._coreService.isCursorHidden && isCursorCell) { - charElement.classList.add(CURSOR_CLASS); - + classes.push(RowCss.CURSOR_CLASS); if (cursorBlink) { - charElement.classList.add(CURSOR_BLINK_CLASS); - } - - switch (cursorStyle) { - case 'bar': - charElement.classList.add(CURSOR_STYLE_BAR_CLASS); - break; - case 'underline': - charElement.classList.add(CURSOR_STYLE_UNDERLINE_CLASS); - break; - default: - charElement.classList.add(CURSOR_STYLE_BLOCK_CLASS); - break; + classes.push(RowCss.CURSOR_BLINK_CLASS); } + classes.push( + cursorStyle === 'bar' + ? RowCss.CURSOR_STYLE_BAR_CLASS + : cursorStyle === 'underline' + ? RowCss.CURSOR_STYLE_UNDERLINE_CLASS + : RowCss.CURSOR_STYLE_BLOCK_CLASS + ); } if (cell.isBold()) { - charElement.classList.add(BOLD_CLASS); + classes.push(RowCss.BOLD_CLASS); } if (cell.isItalic()) { - charElement.classList.add(ITALIC_CLASS); + classes.push(RowCss.ITALIC_CLASS); } if (cell.isDim()) { - charElement.classList.add(DIM_CLASS); + classes.push(RowCss.DIM_CLASS); } if (cell.isInvisible()) { @@ -217,7 +216,7 @@ export class DomRendererRowFactory { } if (cell.isUnderline()) { - charElement.classList.add(`${UNDERLINE_CLASS}-${cell.extended.underlineStyle}`); + classes.push(`${RowCss.UNDERLINE_CLASS}-${cell.extended.underlineStyle}`); if (text === ' ') { text = '\xa0'; // =   } @@ -235,14 +234,14 @@ export class DomRendererRowFactory { } if (cell.isOverline()) { - charElement.classList.add(OVERLINE_CLASS); + classes.push(RowCss.OVERLINE_CLASS); if (text === ' ') { text = '\xa0'; // =   } } if (cell.isStrikethrough()) { - charElement.classList.add(STRIKETHROUGH_CLASS); + classes.push(RowCss.STRIKETHROUGH_CLASS); } // apply link hover underline late, effectively overrides any previous text-decoration settings @@ -304,7 +303,7 @@ export class DomRendererRowFactory { // If it's a top decoration, render above the selection if (isTop) { - charElement.classList.add(`xterm-decoration-top`); + classes.push('xterm-decoration-top'); } // Background @@ -313,7 +312,7 @@ export class DomRendererRowFactory { case Attributes.CM_P16: case Attributes.CM_P256: resolvedBg = colors.ansi[bg]; - charElement.classList.add(`xterm-bg-${bg}`); + classes.push(`xterm-bg-${bg}`); break; case Attributes.CM_RGB: resolvedBg = rgba.toColor(bg >> 16, bg >> 8 & 0xFF, bg & 0xFF); @@ -323,7 +322,7 @@ export class DomRendererRowFactory { default: if (isInverse) { resolvedBg = colors.foreground; - charElement.classList.add(`xterm-bg-${INVERTED_DEFAULT_COLOR}`); + classes.push(`xterm-bg-${INVERTED_DEFAULT_COLOR}`); } else { resolvedBg = colors.background; } @@ -344,7 +343,7 @@ export class DomRendererRowFactory { fg += 8; } if (!this._applyMinimumContrast(charElement, resolvedBg, colors.ansi[fg], cell, bgOverride, undefined)) { - charElement.classList.add(`xterm-fg-${fg}`); + classes.push(`xterm-fg-${fg}`); } break; case Attributes.CM_RGB: @@ -361,11 +360,16 @@ export class DomRendererRowFactory { default: if (!this._applyMinimumContrast(charElement, resolvedBg, colors.foreground, cell, bgOverride, undefined)) { if (isInverse) { - charElement.classList.add(`xterm-fg-${INVERTED_DEFAULT_COLOR}`); + classes.push(`xterm-fg-${INVERTED_DEFAULT_COLOR}`); } } } + if (classes.length) { + charElement.className = classes.join(' '); + classes.length = 0; + } + if (!isCursorCell && !isInSelection && !isJoined) { cellAmount++; } else {