@xterm/xterm 5.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/css/xterm.css +209 -0
- package/lib/xterm.js +2 -0
- package/lib/xterm.js.map +1 -0
- package/package.json +101 -0
- package/src/browser/AccessibilityManager.ts +278 -0
- package/src/browser/Clipboard.ts +93 -0
- package/src/browser/ColorContrastCache.ts +34 -0
- package/src/browser/Lifecycle.ts +33 -0
- package/src/browser/Linkifier2.ts +416 -0
- package/src/browser/LocalizableStrings.ts +12 -0
- package/src/browser/OscLinkProvider.ts +128 -0
- package/src/browser/RenderDebouncer.ts +83 -0
- package/src/browser/Terminal.ts +1317 -0
- package/src/browser/TimeBasedDebouncer.ts +86 -0
- package/src/browser/Types.d.ts +181 -0
- package/src/browser/Viewport.ts +401 -0
- package/src/browser/decorations/BufferDecorationRenderer.ts +134 -0
- package/src/browser/decorations/ColorZoneStore.ts +117 -0
- package/src/browser/decorations/OverviewRulerRenderer.ts +218 -0
- package/src/browser/input/CompositionHelper.ts +246 -0
- package/src/browser/input/Mouse.ts +54 -0
- package/src/browser/input/MoveToCell.ts +249 -0
- package/src/browser/public/Terminal.ts +260 -0
- package/src/browser/renderer/dom/DomRenderer.ts +509 -0
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +526 -0
- package/src/browser/renderer/dom/WidthCache.ts +160 -0
- package/src/browser/renderer/shared/CellColorResolver.ts +137 -0
- package/src/browser/renderer/shared/CharAtlasCache.ts +96 -0
- package/src/browser/renderer/shared/CharAtlasUtils.ts +75 -0
- package/src/browser/renderer/shared/Constants.ts +14 -0
- package/src/browser/renderer/shared/CursorBlinkStateManager.ts +146 -0
- package/src/browser/renderer/shared/CustomGlyphs.ts +687 -0
- package/src/browser/renderer/shared/DevicePixelObserver.ts +41 -0
- package/src/browser/renderer/shared/README.md +1 -0
- package/src/browser/renderer/shared/RendererUtils.ts +58 -0
- package/src/browser/renderer/shared/SelectionRenderModel.ts +91 -0
- package/src/browser/renderer/shared/TextureAtlas.ts +1082 -0
- package/src/browser/renderer/shared/Types.d.ts +173 -0
- package/src/browser/selection/SelectionModel.ts +144 -0
- package/src/browser/selection/Types.d.ts +15 -0
- package/src/browser/services/CharSizeService.ts +102 -0
- package/src/browser/services/CharacterJoinerService.ts +339 -0
- package/src/browser/services/CoreBrowserService.ts +137 -0
- package/src/browser/services/MouseService.ts +46 -0
- package/src/browser/services/RenderService.ts +279 -0
- package/src/browser/services/SelectionService.ts +1031 -0
- package/src/browser/services/Services.ts +147 -0
- package/src/browser/services/ThemeService.ts +237 -0
- package/src/common/CircularList.ts +241 -0
- package/src/common/Clone.ts +23 -0
- package/src/common/Color.ts +357 -0
- package/src/common/CoreTerminal.ts +284 -0
- package/src/common/EventEmitter.ts +78 -0
- package/src/common/InputHandler.ts +3461 -0
- package/src/common/Lifecycle.ts +108 -0
- package/src/common/MultiKeyMap.ts +42 -0
- package/src/common/Platform.ts +44 -0
- package/src/common/SortedList.ts +118 -0
- package/src/common/TaskQueue.ts +166 -0
- package/src/common/TypedArrayUtils.ts +17 -0
- package/src/common/Types.d.ts +553 -0
- package/src/common/WindowsMode.ts +27 -0
- package/src/common/buffer/AttributeData.ts +196 -0
- package/src/common/buffer/Buffer.ts +654 -0
- package/src/common/buffer/BufferLine.ts +524 -0
- package/src/common/buffer/BufferRange.ts +13 -0
- package/src/common/buffer/BufferReflow.ts +223 -0
- package/src/common/buffer/BufferSet.ts +134 -0
- package/src/common/buffer/CellData.ts +94 -0
- package/src/common/buffer/Constants.ts +149 -0
- package/src/common/buffer/Marker.ts +43 -0
- package/src/common/buffer/Types.d.ts +52 -0
- package/src/common/data/Charsets.ts +256 -0
- package/src/common/data/EscapeSequences.ts +153 -0
- package/src/common/input/Keyboard.ts +398 -0
- package/src/common/input/TextDecoder.ts +346 -0
- package/src/common/input/UnicodeV6.ts +145 -0
- package/src/common/input/WriteBuffer.ts +246 -0
- package/src/common/input/XParseColor.ts +80 -0
- package/src/common/parser/Constants.ts +58 -0
- package/src/common/parser/DcsParser.ts +192 -0
- package/src/common/parser/EscapeSequenceParser.ts +792 -0
- package/src/common/parser/OscParser.ts +238 -0
- package/src/common/parser/Params.ts +229 -0
- package/src/common/parser/Types.d.ts +275 -0
- package/src/common/public/AddonManager.ts +53 -0
- package/src/common/public/BufferApiView.ts +35 -0
- package/src/common/public/BufferLineApiView.ts +29 -0
- package/src/common/public/BufferNamespaceApi.ts +36 -0
- package/src/common/public/ParserApi.ts +37 -0
- package/src/common/public/UnicodeApi.ts +27 -0
- package/src/common/services/BufferService.ts +151 -0
- package/src/common/services/CharsetService.ts +34 -0
- package/src/common/services/CoreMouseService.ts +318 -0
- package/src/common/services/CoreService.ts +87 -0
- package/src/common/services/DecorationService.ts +140 -0
- package/src/common/services/InstantiationService.ts +85 -0
- package/src/common/services/LogService.ts +124 -0
- package/src/common/services/OptionsService.ts +202 -0
- package/src/common/services/OscLinkService.ts +115 -0
- package/src/common/services/ServiceRegistry.ts +49 -0
- package/src/common/services/Services.ts +373 -0
- package/src/common/services/UnicodeService.ts +111 -0
- package/src/headless/Terminal.ts +136 -0
- package/src/headless/public/Terminal.ts +195 -0
- package/typings/xterm.d.ts +1857 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory';
|
|
7
|
+
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
|
8
|
+
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
|
9
|
+
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
|
10
|
+
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
|
11
|
+
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
12
|
+
import { ILinkifier2, ILinkifierEvent, ReadonlyColorSet } from 'browser/Types';
|
|
13
|
+
import { color } from 'common/Color';
|
|
14
|
+
import { EventEmitter } from 'common/EventEmitter';
|
|
15
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
16
|
+
import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
|
20
|
+
const ROW_CONTAINER_CLASS = 'xterm-rows';
|
|
21
|
+
const FG_CLASS_PREFIX = 'xterm-fg-';
|
|
22
|
+
const BG_CLASS_PREFIX = 'xterm-bg-';
|
|
23
|
+
const FOCUS_CLASS = 'xterm-focus';
|
|
24
|
+
const SELECTION_CLASS = 'xterm-selection';
|
|
25
|
+
|
|
26
|
+
let nextTerminalId = 1;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A fallback renderer for when canvas is slow. This is not meant to be
|
|
31
|
+
* particularly fast or feature complete, more just stable and usable for when
|
|
32
|
+
* canvas is not an option.
|
|
33
|
+
*/
|
|
34
|
+
export class DomRenderer extends Disposable implements IRenderer {
|
|
35
|
+
private _rowFactory: DomRendererRowFactory;
|
|
36
|
+
private _terminalClass: number = nextTerminalId++;
|
|
37
|
+
|
|
38
|
+
private _themeStyleElement!: HTMLStyleElement;
|
|
39
|
+
private _dimensionsStyleElement!: HTMLStyleElement;
|
|
40
|
+
private _rowContainer: HTMLElement;
|
|
41
|
+
private _rowElements: HTMLElement[] = [];
|
|
42
|
+
private _selectionContainer: HTMLElement;
|
|
43
|
+
private _widthCache: WidthCache;
|
|
44
|
+
|
|
45
|
+
public dimensions: IRenderDimensions;
|
|
46
|
+
|
|
47
|
+
public readonly onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>()).event;
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
private readonly _document: Document,
|
|
51
|
+
private readonly _element: HTMLElement,
|
|
52
|
+
private readonly _screenElement: HTMLElement,
|
|
53
|
+
private readonly _viewportElement: HTMLElement,
|
|
54
|
+
private readonly _helperContainer: HTMLElement,
|
|
55
|
+
private readonly _linkifier2: ILinkifier2,
|
|
56
|
+
@IInstantiationService instantiationService: IInstantiationService,
|
|
57
|
+
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
|
58
|
+
@IOptionsService private readonly _optionsService: IOptionsService,
|
|
59
|
+
@IBufferService private readonly _bufferService: IBufferService,
|
|
60
|
+
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
|
61
|
+
@IThemeService private readonly _themeService: IThemeService
|
|
62
|
+
) {
|
|
63
|
+
super();
|
|
64
|
+
this._rowContainer = this._document.createElement('div');
|
|
65
|
+
this._rowContainer.classList.add(ROW_CONTAINER_CLASS);
|
|
66
|
+
this._rowContainer.style.lineHeight = 'normal';
|
|
67
|
+
this._rowContainer.setAttribute('aria-hidden', 'true');
|
|
68
|
+
this._refreshRowElements(this._bufferService.cols, this._bufferService.rows);
|
|
69
|
+
this._selectionContainer = this._document.createElement('div');
|
|
70
|
+
this._selectionContainer.classList.add(SELECTION_CLASS);
|
|
71
|
+
this._selectionContainer.setAttribute('aria-hidden', 'true');
|
|
72
|
+
|
|
73
|
+
this.dimensions = createRenderDimensions();
|
|
74
|
+
this._updateDimensions();
|
|
75
|
+
this.register(this._optionsService.onOptionChange(() => this._handleOptionsChanged()));
|
|
76
|
+
|
|
77
|
+
this.register(this._themeService.onChangeColors(e => this._injectCss(e)));
|
|
78
|
+
this._injectCss(this._themeService.colors);
|
|
79
|
+
|
|
80
|
+
this._rowFactory = instantiationService.createInstance(DomRendererRowFactory, document);
|
|
81
|
+
|
|
82
|
+
this._element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
|
83
|
+
this._screenElement.appendChild(this._rowContainer);
|
|
84
|
+
this._screenElement.appendChild(this._selectionContainer);
|
|
85
|
+
|
|
86
|
+
this.register(this._linkifier2.onShowLinkUnderline(e => this._handleLinkHover(e)));
|
|
87
|
+
this.register(this._linkifier2.onHideLinkUnderline(e => this._handleLinkLeave(e)));
|
|
88
|
+
|
|
89
|
+
this.register(toDisposable(() => {
|
|
90
|
+
this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
|
91
|
+
|
|
92
|
+
// Outside influences such as React unmounts may manipulate the DOM before our disposal.
|
|
93
|
+
// https://github.com/xtermjs/xterm.js/issues/2960
|
|
94
|
+
this._rowContainer.remove();
|
|
95
|
+
this._selectionContainer.remove();
|
|
96
|
+
this._widthCache.dispose();
|
|
97
|
+
this._themeStyleElement.remove();
|
|
98
|
+
this._dimensionsStyleElement.remove();
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
this._widthCache = new WidthCache(this._document, this._helperContainer);
|
|
102
|
+
this._widthCache.setFont(
|
|
103
|
+
this._optionsService.rawOptions.fontFamily,
|
|
104
|
+
this._optionsService.rawOptions.fontSize,
|
|
105
|
+
this._optionsService.rawOptions.fontWeight,
|
|
106
|
+
this._optionsService.rawOptions.fontWeightBold
|
|
107
|
+
);
|
|
108
|
+
this._setDefaultSpacing();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private _updateDimensions(): void {
|
|
112
|
+
const dpr = this._coreBrowserService.dpr;
|
|
113
|
+
this.dimensions.device.char.width = this._charSizeService.width * dpr;
|
|
114
|
+
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * dpr);
|
|
115
|
+
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);
|
|
116
|
+
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
|
|
117
|
+
this.dimensions.device.char.left = 0;
|
|
118
|
+
this.dimensions.device.char.top = 0;
|
|
119
|
+
this.dimensions.device.canvas.width = this.dimensions.device.cell.width * this._bufferService.cols;
|
|
120
|
+
this.dimensions.device.canvas.height = this.dimensions.device.cell.height * this._bufferService.rows;
|
|
121
|
+
this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / dpr);
|
|
122
|
+
this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / dpr);
|
|
123
|
+
this.dimensions.css.cell.width = this.dimensions.css.canvas.width / this._bufferService.cols;
|
|
124
|
+
this.dimensions.css.cell.height = this.dimensions.css.canvas.height / this._bufferService.rows;
|
|
125
|
+
|
|
126
|
+
for (const element of this._rowElements) {
|
|
127
|
+
element.style.width = `${this.dimensions.css.canvas.width}px`;
|
|
128
|
+
element.style.height = `${this.dimensions.css.cell.height}px`;
|
|
129
|
+
element.style.lineHeight = `${this.dimensions.css.cell.height}px`;
|
|
130
|
+
// Make sure rows don't overflow onto following row
|
|
131
|
+
element.style.overflow = 'hidden';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!this._dimensionsStyleElement) {
|
|
135
|
+
this._dimensionsStyleElement = this._document.createElement('style');
|
|
136
|
+
this._screenElement.appendChild(this._dimensionsStyleElement);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const styles =
|
|
140
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` +
|
|
141
|
+
` display: inline-block;` + // TODO: find workaround for inline-block (creates ~20% render penalty)
|
|
142
|
+
` height: 100%;` +
|
|
143
|
+
` vertical-align: top;` +
|
|
144
|
+
`}`;
|
|
145
|
+
|
|
146
|
+
this._dimensionsStyleElement.textContent = styles;
|
|
147
|
+
|
|
148
|
+
this._selectionContainer.style.height = this._viewportElement.style.height;
|
|
149
|
+
this._screenElement.style.width = `${this.dimensions.css.canvas.width}px`;
|
|
150
|
+
this._screenElement.style.height = `${this.dimensions.css.canvas.height}px`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private _injectCss(colors: ReadonlyColorSet): void {
|
|
154
|
+
if (!this._themeStyleElement) {
|
|
155
|
+
this._themeStyleElement = this._document.createElement('style');
|
|
156
|
+
this._screenElement.appendChild(this._themeStyleElement);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Base CSS
|
|
160
|
+
let styles =
|
|
161
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
|
|
162
|
+
` color: ${colors.foreground.css};` +
|
|
163
|
+
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
|
|
164
|
+
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
|
|
165
|
+
` font-kerning: none;` +
|
|
166
|
+
` white-space: pre` +
|
|
167
|
+
`}`;
|
|
168
|
+
styles +=
|
|
169
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .xterm-dim {` +
|
|
170
|
+
` color: ${color.multiplyOpacity(colors.foreground, 0.5).css};` +
|
|
171
|
+
`}`;
|
|
172
|
+
// Text styles
|
|
173
|
+
styles +=
|
|
174
|
+
`${this._terminalSelector} span:not(.${RowCss.BOLD_CLASS}) {` +
|
|
175
|
+
` font-weight: ${this._optionsService.rawOptions.fontWeight};` +
|
|
176
|
+
`}` +
|
|
177
|
+
`${this._terminalSelector} span.${RowCss.BOLD_CLASS} {` +
|
|
178
|
+
` font-weight: ${this._optionsService.rawOptions.fontWeightBold};` +
|
|
179
|
+
`}` +
|
|
180
|
+
`${this._terminalSelector} span.${RowCss.ITALIC_CLASS} {` +
|
|
181
|
+
` font-style: italic;` +
|
|
182
|
+
`}`;
|
|
183
|
+
// Blink animation
|
|
184
|
+
styles +=
|
|
185
|
+
`@keyframes blink_box_shadow` + `_` + this._terminalClass + ` {` +
|
|
186
|
+
` 50% {` +
|
|
187
|
+
` border-bottom-style: hidden;` +
|
|
188
|
+
` }` +
|
|
189
|
+
`}`;
|
|
190
|
+
styles +=
|
|
191
|
+
`@keyframes blink_block` + `_` + this._terminalClass + ` {` +
|
|
192
|
+
` 0% {` +
|
|
193
|
+
` background-color: ${colors.cursor.css};` +
|
|
194
|
+
` color: ${colors.cursorAccent.css};` +
|
|
195
|
+
` }` +
|
|
196
|
+
` 50% {` +
|
|
197
|
+
` background-color: inherit;` +
|
|
198
|
+
` color: ${colors.cursor.css};` +
|
|
199
|
+
` }` +
|
|
200
|
+
`}`;
|
|
201
|
+
// Cursor
|
|
202
|
+
styles +=
|
|
203
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}:not(.${RowCss.CURSOR_STYLE_BLOCK_CLASS}) {` +
|
|
204
|
+
` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
|
205
|
+
`}` +
|
|
206
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
|
207
|
+
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
|
208
|
+
`}` +
|
|
209
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
|
210
|
+
` background-color: ${colors.cursor.css} !important;` +
|
|
211
|
+
` color: ${colors.cursorAccent.css} !important;` +
|
|
212
|
+
`}` +
|
|
213
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
|
|
214
|
+
` outline: 1px solid ${colors.cursor.css};` +
|
|
215
|
+
` outline-offset: -1px;` +
|
|
216
|
+
`}` +
|
|
217
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` +
|
|
218
|
+
` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` +
|
|
219
|
+
`}` +
|
|
220
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
|
221
|
+
` border-bottom: 1px ${colors.cursor.css};` +
|
|
222
|
+
` border-bottom-style: solid;` +
|
|
223
|
+
` height: calc(100% - 1px);` +
|
|
224
|
+
`}`;
|
|
225
|
+
// Selection
|
|
226
|
+
styles +=
|
|
227
|
+
`${this._terminalSelector} .${SELECTION_CLASS} {` +
|
|
228
|
+
` position: absolute;` +
|
|
229
|
+
` top: 0;` +
|
|
230
|
+
` left: 0;` +
|
|
231
|
+
` z-index: 1;` +
|
|
232
|
+
` pointer-events: none;` +
|
|
233
|
+
`}` +
|
|
234
|
+
`${this._terminalSelector}.focus .${SELECTION_CLASS} div {` +
|
|
235
|
+
` position: absolute;` +
|
|
236
|
+
` background-color: ${colors.selectionBackgroundOpaque.css};` +
|
|
237
|
+
`}` +
|
|
238
|
+
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
|
|
239
|
+
` position: absolute;` +
|
|
240
|
+
` background-color: ${colors.selectionInactiveBackgroundOpaque.css};` +
|
|
241
|
+
`}`;
|
|
242
|
+
// Colors
|
|
243
|
+
for (const [i, c] of colors.ansi.entries()) {
|
|
244
|
+
styles +=
|
|
245
|
+
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` +
|
|
246
|
+
`${this._terminalSelector} .${FG_CLASS_PREFIX}${i}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(c, 0.5).css}; }` +
|
|
247
|
+
`${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`;
|
|
248
|
+
}
|
|
249
|
+
styles +=
|
|
250
|
+
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(colors.background).css}; }` +
|
|
251
|
+
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${RowCss.DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
|
|
252
|
+
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
|
|
253
|
+
|
|
254
|
+
this._themeStyleElement.textContent = styles;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* default letter spacing
|
|
259
|
+
* Due to rounding issues in dimensions dpr calc glyph might render
|
|
260
|
+
* slightly too wide or too narrow. The method corrects the stacking offsets
|
|
261
|
+
* by applying a default letter-spacing for all chars.
|
|
262
|
+
* The value gets passed to the row factory to avoid setting this value again
|
|
263
|
+
* (render speedup is roughly 10%).
|
|
264
|
+
*/
|
|
265
|
+
private _setDefaultSpacing(): void {
|
|
266
|
+
// measure same char as in CharSizeService to get the base deviation
|
|
267
|
+
const spacing = this.dimensions.css.cell.width - this._widthCache.get('W', false, false);
|
|
268
|
+
this._rowContainer.style.letterSpacing = `${spacing}px`;
|
|
269
|
+
this._rowFactory.defaultSpacing = spacing;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
public handleDevicePixelRatioChange(): void {
|
|
273
|
+
this._updateDimensions();
|
|
274
|
+
this._widthCache.clear();
|
|
275
|
+
this._setDefaultSpacing();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private _refreshRowElements(cols: number, rows: number): void {
|
|
279
|
+
// Add missing elements
|
|
280
|
+
for (let i = this._rowElements.length; i <= rows; i++) {
|
|
281
|
+
const row = this._document.createElement('div');
|
|
282
|
+
this._rowContainer.appendChild(row);
|
|
283
|
+
this._rowElements.push(row);
|
|
284
|
+
}
|
|
285
|
+
// Remove excess elements
|
|
286
|
+
while (this._rowElements.length > rows) {
|
|
287
|
+
this._rowContainer.removeChild(this._rowElements.pop()!);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public handleResize(cols: number, rows: number): void {
|
|
292
|
+
this._refreshRowElements(cols, rows);
|
|
293
|
+
this._updateDimensions();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
public handleCharSizeChanged(): void {
|
|
297
|
+
this._updateDimensions();
|
|
298
|
+
this._widthCache.clear();
|
|
299
|
+
this._setDefaultSpacing();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public handleBlur(): void {
|
|
303
|
+
this._rowContainer.classList.remove(FOCUS_CLASS);
|
|
304
|
+
this.renderRows(0, this._bufferService.rows - 1);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
public handleFocus(): void {
|
|
308
|
+
this._rowContainer.classList.add(FOCUS_CLASS);
|
|
309
|
+
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
|
313
|
+
// Remove all selections
|
|
314
|
+
this._selectionContainer.replaceChildren();
|
|
315
|
+
this._rowFactory.handleSelectionChanged(start, end, columnSelectMode);
|
|
316
|
+
this.renderRows(0, this._bufferService.rows - 1);
|
|
317
|
+
|
|
318
|
+
// Selection does not exist
|
|
319
|
+
if (!start || !end) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Translate from buffer position to viewport position
|
|
324
|
+
const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
|
|
325
|
+
const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
|
|
326
|
+
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
|
327
|
+
const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
|
|
328
|
+
|
|
329
|
+
// No need to draw the selection
|
|
330
|
+
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Create the selections
|
|
335
|
+
const documentFragment = this._document.createDocumentFragment();
|
|
336
|
+
|
|
337
|
+
if (columnSelectMode) {
|
|
338
|
+
const isXFlipped = start[0] > end[0];
|
|
339
|
+
documentFragment.appendChild(
|
|
340
|
+
this._createSelectionElement(viewportCappedStartRow, isXFlipped ? end[0] : start[0], isXFlipped ? start[0] : end[0], viewportCappedEndRow - viewportCappedStartRow + 1)
|
|
341
|
+
);
|
|
342
|
+
} else {
|
|
343
|
+
// Draw first row
|
|
344
|
+
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
|
345
|
+
const endCol = viewportCappedStartRow === viewportEndRow ? end[0] : this._bufferService.cols;
|
|
346
|
+
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
|
|
347
|
+
// Draw middle rows
|
|
348
|
+
const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1;
|
|
349
|
+
documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._bufferService.cols, middleRowsCount));
|
|
350
|
+
// Draw final row
|
|
351
|
+
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
|
352
|
+
// Only draw viewportEndRow if it's not the same as viewporttartRow
|
|
353
|
+
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
|
|
354
|
+
documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
this._selectionContainer.appendChild(documentFragment);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Creates a selection element at the specified position.
|
|
362
|
+
* @param row The row of the selection.
|
|
363
|
+
* @param colStart The start column.
|
|
364
|
+
* @param colEnd The end columns.
|
|
365
|
+
*/
|
|
366
|
+
private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement {
|
|
367
|
+
const element = this._document.createElement('div');
|
|
368
|
+
element.style.height = `${rowCount * this.dimensions.css.cell.height}px`;
|
|
369
|
+
element.style.top = `${row * this.dimensions.css.cell.height}px`;
|
|
370
|
+
element.style.left = `${colStart * this.dimensions.css.cell.width}px`;
|
|
371
|
+
element.style.width = `${this.dimensions.css.cell.width * (colEnd - colStart)}px`;
|
|
372
|
+
return element;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
public handleCursorMove(): void {
|
|
376
|
+
// No-op, the cursor is drawn when rows are drawn
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private _handleOptionsChanged(): void {
|
|
380
|
+
// Force a refresh
|
|
381
|
+
this._updateDimensions();
|
|
382
|
+
// Refresh CSS
|
|
383
|
+
this._injectCss(this._themeService.colors);
|
|
384
|
+
// update spacing cache
|
|
385
|
+
this._widthCache.setFont(
|
|
386
|
+
this._optionsService.rawOptions.fontFamily,
|
|
387
|
+
this._optionsService.rawOptions.fontSize,
|
|
388
|
+
this._optionsService.rawOptions.fontWeight,
|
|
389
|
+
this._optionsService.rawOptions.fontWeightBold
|
|
390
|
+
);
|
|
391
|
+
this._setDefaultSpacing();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
public clear(): void {
|
|
395
|
+
for (const e of this._rowElements) {
|
|
396
|
+
/**
|
|
397
|
+
* NOTE: This used to be `e.innerText = '';` but that doesn't work when using `jsdom` and
|
|
398
|
+
* `@testing-library/react`
|
|
399
|
+
*
|
|
400
|
+
* references:
|
|
401
|
+
* - https://github.com/testing-library/react-testing-library/issues/1146
|
|
402
|
+
* - https://github.com/jsdom/jsdom/issues/1245
|
|
403
|
+
*/
|
|
404
|
+
e.replaceChildren();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public renderRows(start: number, end: number): void {
|
|
409
|
+
const buffer = this._bufferService.buffer;
|
|
410
|
+
const cursorAbsoluteY = buffer.ybase + buffer.y;
|
|
411
|
+
const cursorX = Math.min(buffer.x, this._bufferService.cols - 1);
|
|
412
|
+
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
|
413
|
+
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
|
414
|
+
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
|
415
|
+
|
|
416
|
+
for (let y = start; y <= end; y++) {
|
|
417
|
+
const row = y + buffer.ydisp;
|
|
418
|
+
const rowElement = this._rowElements[y];
|
|
419
|
+
const lineData = buffer.lines.get(row);
|
|
420
|
+
if (!rowElement || !lineData) {
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
rowElement.replaceChildren(
|
|
424
|
+
...this._rowFactory.createRow(
|
|
425
|
+
lineData,
|
|
426
|
+
row,
|
|
427
|
+
row === cursorAbsoluteY,
|
|
428
|
+
cursorStyle,
|
|
429
|
+
cursorInactiveStyle,
|
|
430
|
+
cursorX,
|
|
431
|
+
cursorBlink,
|
|
432
|
+
this.dimensions.css.cell.width,
|
|
433
|
+
this._widthCache,
|
|
434
|
+
-1,
|
|
435
|
+
-1
|
|
436
|
+
)
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private get _terminalSelector(): string {
|
|
442
|
+
return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private _handleLinkHover(e: ILinkifierEvent): void {
|
|
446
|
+
this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, true);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private _handleLinkLeave(e: ILinkifierEvent): void {
|
|
450
|
+
this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, false);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void {
|
|
454
|
+
/**
|
|
455
|
+
* NOTE: The linkifier may send out of viewport y-values if:
|
|
456
|
+
* - negative y-value: the link started at a higher line
|
|
457
|
+
* - y-value >= maxY: the link ends at a line below viewport
|
|
458
|
+
*
|
|
459
|
+
* For negative y-values we can simply adjust x = 0,
|
|
460
|
+
* as higher up link start means, that everything from
|
|
461
|
+
* (0,0) is a link under top-down-left-right char progression
|
|
462
|
+
*
|
|
463
|
+
* Additionally there might be a small chance of out-of-sync x|y-values
|
|
464
|
+
* from a race condition of render updates vs. link event handler execution:
|
|
465
|
+
* - (sync) resize: chances terminal buffer in sync, schedules render update async
|
|
466
|
+
* - (async) link handler race condition: new buffer metrics, but still on old render state
|
|
467
|
+
* - (async) render update: brings term metrics and render state back in sync
|
|
468
|
+
*/
|
|
469
|
+
// clip coords into viewport
|
|
470
|
+
if (y < 0) x = 0;
|
|
471
|
+
if (y2 < 0) x2 = 0;
|
|
472
|
+
const maxY = this._bufferService.rows - 1;
|
|
473
|
+
y = Math.max(Math.min(y, maxY), 0);
|
|
474
|
+
y2 = Math.max(Math.min(y2, maxY), 0);
|
|
475
|
+
|
|
476
|
+
cols = Math.min(cols, this._bufferService.cols);
|
|
477
|
+
const buffer = this._bufferService.buffer;
|
|
478
|
+
const cursorAbsoluteY = buffer.ybase + buffer.y;
|
|
479
|
+
const cursorX = Math.min(buffer.x, cols - 1);
|
|
480
|
+
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
|
481
|
+
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
|
482
|
+
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
|
483
|
+
|
|
484
|
+
// refresh rows within link range
|
|
485
|
+
for (let i = y; i <= y2; ++i) {
|
|
486
|
+
const row = i + buffer.ydisp;
|
|
487
|
+
const rowElement = this._rowElements[i];
|
|
488
|
+
const bufferline = buffer.lines.get(row);
|
|
489
|
+
if (!rowElement || !bufferline) {
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
rowElement.replaceChildren(
|
|
493
|
+
...this._rowFactory.createRow(
|
|
494
|
+
bufferline,
|
|
495
|
+
row,
|
|
496
|
+
row === cursorAbsoluteY,
|
|
497
|
+
cursorStyle,
|
|
498
|
+
cursorInactiveStyle,
|
|
499
|
+
cursorX,
|
|
500
|
+
cursorBlink,
|
|
501
|
+
this.dimensions.css.cell.width,
|
|
502
|
+
this._widthCache,
|
|
503
|
+
enabled ? (i === y ? x : 0) : -1,
|
|
504
|
+
enabled ? ((i === y2 ? x2 : cols) - 1) : -1
|
|
505
|
+
)
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|