@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,524 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from 'common/Types';
|
|
7
|
+
import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData';
|
|
8
|
+
import { CellData } from 'common/buffer/CellData';
|
|
9
|
+
import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
|
10
|
+
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* buffer memory layout:
|
|
14
|
+
*
|
|
15
|
+
* | uint32_t | uint32_t | uint32_t |
|
|
16
|
+
* | `content` | `FG` | `BG` |
|
|
17
|
+
* | wcwidth(2) comb(1) codepoint(21) | flags(8) R(8) G(8) B(8) | flags(8) R(8) G(8) B(8) |
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/** typed array slots taken by one cell */
|
|
22
|
+
const CELL_SIZE = 3;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Cell member indices.
|
|
26
|
+
*
|
|
27
|
+
* Direct access:
|
|
28
|
+
* `content = data[column * CELL_SIZE + Cell.CONTENT];`
|
|
29
|
+
* `fg = data[column * CELL_SIZE + Cell.FG];`
|
|
30
|
+
* `bg = data[column * CELL_SIZE + Cell.BG];`
|
|
31
|
+
*/
|
|
32
|
+
const enum Cell {
|
|
33
|
+
CONTENT = 0,
|
|
34
|
+
FG = 1, // currently simply holds all known attrs
|
|
35
|
+
BG = 2 // currently unused
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData());
|
|
39
|
+
|
|
40
|
+
// Work variables to avoid garbage collection
|
|
41
|
+
let $startIndex = 0;
|
|
42
|
+
|
|
43
|
+
/** Factor when to cleanup underlying array buffer after shrinking. */
|
|
44
|
+
const CLEANUP_THRESHOLD = 2;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Typed array based bufferline implementation.
|
|
48
|
+
*
|
|
49
|
+
* There are 2 ways to insert data into the cell buffer:
|
|
50
|
+
* - `setCellFromCodepoint` + `addCodepointToCell`
|
|
51
|
+
* Use these for data that is already UTF32.
|
|
52
|
+
* Used during normal input in `InputHandler` for faster buffer access.
|
|
53
|
+
* - `setCell`
|
|
54
|
+
* This method takes a CellData object and stores the data in the buffer.
|
|
55
|
+
* Use `CellData.fromCharData` to create the CellData object (e.g. from JS string).
|
|
56
|
+
*
|
|
57
|
+
* To retrieve data from the buffer use either one of the primitive methods
|
|
58
|
+
* (if only one particular value is needed) or `loadCell`. For `loadCell` in a loop
|
|
59
|
+
* memory allocs / GC pressure can be greatly reduced by reusing the CellData object.
|
|
60
|
+
*/
|
|
61
|
+
export class BufferLine implements IBufferLine {
|
|
62
|
+
protected _data: Uint32Array;
|
|
63
|
+
protected _combined: {[index: number]: string} = {};
|
|
64
|
+
protected _extendedAttrs: {[index: number]: IExtendedAttrs | undefined} = {};
|
|
65
|
+
public length: number;
|
|
66
|
+
|
|
67
|
+
constructor(cols: number, fillCellData?: ICellData, public isWrapped: boolean = false) {
|
|
68
|
+
this._data = new Uint32Array(cols * CELL_SIZE);
|
|
69
|
+
const cell = fillCellData || CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
|
70
|
+
for (let i = 0; i < cols; ++i) {
|
|
71
|
+
this.setCell(i, cell);
|
|
72
|
+
}
|
|
73
|
+
this.length = cols;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get cell data CharData.
|
|
78
|
+
* @deprecated
|
|
79
|
+
*/
|
|
80
|
+
public get(index: number): CharData {
|
|
81
|
+
const content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
|
82
|
+
const cp = content & Content.CODEPOINT_MASK;
|
|
83
|
+
return [
|
|
84
|
+
this._data[index * CELL_SIZE + Cell.FG],
|
|
85
|
+
(content & Content.IS_COMBINED_MASK)
|
|
86
|
+
? this._combined[index]
|
|
87
|
+
: (cp) ? stringFromCodePoint(cp) : '',
|
|
88
|
+
content >> Content.WIDTH_SHIFT,
|
|
89
|
+
(content & Content.IS_COMBINED_MASK)
|
|
90
|
+
? this._combined[index].charCodeAt(this._combined[index].length - 1)
|
|
91
|
+
: cp
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Set cell data from CharData.
|
|
97
|
+
* @deprecated
|
|
98
|
+
*/
|
|
99
|
+
public set(index: number, value: CharData): void {
|
|
100
|
+
this._data[index * CELL_SIZE + Cell.FG] = value[CHAR_DATA_ATTR_INDEX];
|
|
101
|
+
if (value[CHAR_DATA_CHAR_INDEX].length > 1) {
|
|
102
|
+
this._combined[index] = value[1];
|
|
103
|
+
this._data[index * CELL_SIZE + Cell.CONTENT] = index | Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
|
104
|
+
} else {
|
|
105
|
+
this._data[index * CELL_SIZE + Cell.CONTENT] = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* primitive getters
|
|
111
|
+
* use these when only one value is needed, otherwise use `loadCell`
|
|
112
|
+
*/
|
|
113
|
+
public getWidth(index: number): number {
|
|
114
|
+
return this._data[index * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Test whether content has width. */
|
|
118
|
+
public hasWidth(index: number): number {
|
|
119
|
+
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.WIDTH_MASK;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Get FG cell component. */
|
|
123
|
+
public getFg(index: number): number {
|
|
124
|
+
return this._data[index * CELL_SIZE + Cell.FG];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Get BG cell component. */
|
|
128
|
+
public getBg(index: number): number {
|
|
129
|
+
return this._data[index * CELL_SIZE + Cell.BG];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Test whether contains any chars.
|
|
134
|
+
* Basically an empty has no content, but other cells might differ in FG/BG
|
|
135
|
+
* from real empty cells.
|
|
136
|
+
*/
|
|
137
|
+
public hasContent(index: number): number {
|
|
138
|
+
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get codepoint of the cell.
|
|
143
|
+
* To be in line with `code` in CharData this either returns
|
|
144
|
+
* a single UTF32 codepoint or the last codepoint of a combined string.
|
|
145
|
+
*/
|
|
146
|
+
public getCodePoint(index: number): number {
|
|
147
|
+
const content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
|
148
|
+
if (content & Content.IS_COMBINED_MASK) {
|
|
149
|
+
return this._combined[index].charCodeAt(this._combined[index].length - 1);
|
|
150
|
+
}
|
|
151
|
+
return content & Content.CODEPOINT_MASK;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Test whether the cell contains a combined string. */
|
|
155
|
+
public isCombined(index: number): number {
|
|
156
|
+
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.IS_COMBINED_MASK;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Returns the string content of the cell. */
|
|
160
|
+
public getString(index: number): string {
|
|
161
|
+
const content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
|
162
|
+
if (content & Content.IS_COMBINED_MASK) {
|
|
163
|
+
return this._combined[index];
|
|
164
|
+
}
|
|
165
|
+
if (content & Content.CODEPOINT_MASK) {
|
|
166
|
+
return stringFromCodePoint(content & Content.CODEPOINT_MASK);
|
|
167
|
+
}
|
|
168
|
+
// return empty string for empty cells
|
|
169
|
+
return '';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Get state of protected flag. */
|
|
173
|
+
public isProtected(index: number): number {
|
|
174
|
+
return this._data[index * CELL_SIZE + Cell.BG] & BgFlags.PROTECTED;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Load data at `index` into `cell`. This is used to access cells in a way that's more friendly
|
|
179
|
+
* to GC as it significantly reduced the amount of new objects/references needed.
|
|
180
|
+
*/
|
|
181
|
+
public loadCell(index: number, cell: ICellData): ICellData {
|
|
182
|
+
$startIndex = index * CELL_SIZE;
|
|
183
|
+
cell.content = this._data[$startIndex + Cell.CONTENT];
|
|
184
|
+
cell.fg = this._data[$startIndex + Cell.FG];
|
|
185
|
+
cell.bg = this._data[$startIndex + Cell.BG];
|
|
186
|
+
if (cell.content & Content.IS_COMBINED_MASK) {
|
|
187
|
+
cell.combinedData = this._combined[index];
|
|
188
|
+
}
|
|
189
|
+
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
|
190
|
+
cell.extended = this._extendedAttrs[index]!;
|
|
191
|
+
}
|
|
192
|
+
return cell;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Set data at `index` to `cell`.
|
|
197
|
+
*/
|
|
198
|
+
public setCell(index: number, cell: ICellData): void {
|
|
199
|
+
if (cell.content & Content.IS_COMBINED_MASK) {
|
|
200
|
+
this._combined[index] = cell.combinedData;
|
|
201
|
+
}
|
|
202
|
+
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
|
203
|
+
this._extendedAttrs[index] = cell.extended;
|
|
204
|
+
}
|
|
205
|
+
this._data[index * CELL_SIZE + Cell.CONTENT] = cell.content;
|
|
206
|
+
this._data[index * CELL_SIZE + Cell.FG] = cell.fg;
|
|
207
|
+
this._data[index * CELL_SIZE + Cell.BG] = cell.bg;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Set cell data from input handler.
|
|
212
|
+
* Since the input handler see the incoming chars as UTF32 codepoints,
|
|
213
|
+
* it gets an optimized access method.
|
|
214
|
+
*/
|
|
215
|
+
public setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs): void {
|
|
216
|
+
if (bg & BgFlags.HAS_EXTENDED) {
|
|
217
|
+
this._extendedAttrs[index] = eAttrs;
|
|
218
|
+
}
|
|
219
|
+
this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
|
220
|
+
this._data[index * CELL_SIZE + Cell.FG] = fg;
|
|
221
|
+
this._data[index * CELL_SIZE + Cell.BG] = bg;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Add a codepoint to a cell from input handler.
|
|
226
|
+
* During input stage combining chars with a width of 0 follow and stack
|
|
227
|
+
* onto a leading char. Since we already set the attrs
|
|
228
|
+
* by the previous `setDataFromCodePoint` call, we can omit it here.
|
|
229
|
+
*/
|
|
230
|
+
public addCodepointToCell(index: number, codePoint: number, width: number): void {
|
|
231
|
+
let content = this._data[index * CELL_SIZE + Cell.CONTENT];
|
|
232
|
+
if (content & Content.IS_COMBINED_MASK) {
|
|
233
|
+
// we already have a combined string, simply add
|
|
234
|
+
this._combined[index] += stringFromCodePoint(codePoint);
|
|
235
|
+
} else {
|
|
236
|
+
if (content & Content.CODEPOINT_MASK) {
|
|
237
|
+
// normal case for combining chars:
|
|
238
|
+
// - move current leading char + new one into combined string
|
|
239
|
+
// - set combined flag
|
|
240
|
+
this._combined[index] = stringFromCodePoint(content & Content.CODEPOINT_MASK) + stringFromCodePoint(codePoint);
|
|
241
|
+
content &= ~Content.CODEPOINT_MASK; // set codepoint in buffer to 0
|
|
242
|
+
content |= Content.IS_COMBINED_MASK;
|
|
243
|
+
} else {
|
|
244
|
+
// should not happen - we actually have no data in the cell yet
|
|
245
|
+
// simply set the data in the cell buffer with a width of 1
|
|
246
|
+
content = codePoint | (1 << Content.WIDTH_SHIFT);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (width) {
|
|
250
|
+
content &= ~Content.WIDTH_MASK;
|
|
251
|
+
content |= width << Content.WIDTH_SHIFT;
|
|
252
|
+
}
|
|
253
|
+
this._data[index * CELL_SIZE + Cell.CONTENT] = content;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
public insertCells(pos: number, n: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
|
257
|
+
pos %= this.length;
|
|
258
|
+
|
|
259
|
+
// handle fullwidth at pos: reset cell one to the left if pos is second cell of a wide char
|
|
260
|
+
if (pos && this.getWidth(pos - 1) === 2) {
|
|
261
|
+
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (n < this.length - pos) {
|
|
265
|
+
const cell = new CellData();
|
|
266
|
+
for (let i = this.length - pos - n - 1; i >= 0; --i) {
|
|
267
|
+
this.setCell(pos + n + i, this.loadCell(pos + i, cell));
|
|
268
|
+
}
|
|
269
|
+
for (let i = 0; i < n; ++i) {
|
|
270
|
+
this.setCell(pos + i, fillCellData);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
for (let i = pos; i < this.length; ++i) {
|
|
274
|
+
this.setCell(i, fillCellData);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// handle fullwidth at line end: reset last cell if it is first cell of a wide char
|
|
279
|
+
if (this.getWidth(this.length - 1) === 2) {
|
|
280
|
+
this.setCellFromCodePoint(this.length - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
public deleteCells(pos: number, n: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
|
285
|
+
pos %= this.length;
|
|
286
|
+
if (n < this.length - pos) {
|
|
287
|
+
const cell = new CellData();
|
|
288
|
+
for (let i = 0; i < this.length - pos - n; ++i) {
|
|
289
|
+
this.setCell(pos + i, this.loadCell(pos + n + i, cell));
|
|
290
|
+
}
|
|
291
|
+
for (let i = this.length - n; i < this.length; ++i) {
|
|
292
|
+
this.setCell(i, fillCellData);
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
for (let i = pos; i < this.length; ++i) {
|
|
296
|
+
this.setCell(i, fillCellData);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// handle fullwidth at pos:
|
|
301
|
+
// - reset pos-1 if wide char
|
|
302
|
+
// - reset pos if width==0 (previous second cell of a wide char)
|
|
303
|
+
if (pos && this.getWidth(pos - 1) === 2) {
|
|
304
|
+
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
305
|
+
}
|
|
306
|
+
if (this.getWidth(pos) === 0 && !this.hasContent(pos)) {
|
|
307
|
+
this.setCellFromCodePoint(pos, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
public replaceCells(start: number, end: number, fillCellData: ICellData, eraseAttr?: IAttributeData, respectProtect: boolean = false): void {
|
|
312
|
+
// full branching on respectProtect==true, hopefully getting fast JIT for standard case
|
|
313
|
+
if (respectProtect) {
|
|
314
|
+
if (start && this.getWidth(start - 1) === 2 && !this.isProtected(start - 1)) {
|
|
315
|
+
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
316
|
+
}
|
|
317
|
+
if (end < this.length && this.getWidth(end - 1) === 2 && !this.isProtected(end)) {
|
|
318
|
+
this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
319
|
+
}
|
|
320
|
+
while (start < end && start < this.length) {
|
|
321
|
+
if (!this.isProtected(start)) {
|
|
322
|
+
this.setCell(start, fillCellData);
|
|
323
|
+
}
|
|
324
|
+
start++;
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// handle fullwidth at start: reset cell one to the left if start is second cell of a wide char
|
|
330
|
+
if (start && this.getWidth(start - 1) === 2) {
|
|
331
|
+
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
332
|
+
}
|
|
333
|
+
// handle fullwidth at last cell + 1: reset to empty cell if it is second part of a wide char
|
|
334
|
+
if (end < this.length && this.getWidth(end - 1) === 2) {
|
|
335
|
+
this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
while (start < end && start < this.length) {
|
|
339
|
+
this.setCell(start++, fillCellData);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Resize BufferLine to `cols` filling excess cells with `fillCellData`.
|
|
345
|
+
* The underlying array buffer will not change if there is still enough space
|
|
346
|
+
* to hold the new buffer line data.
|
|
347
|
+
* Returns a boolean indicating, whether a `cleanupMemory` call would free
|
|
348
|
+
* excess memory (true after shrinking > CLEANUP_THRESHOLD).
|
|
349
|
+
*/
|
|
350
|
+
public resize(cols: number, fillCellData: ICellData): boolean {
|
|
351
|
+
if (cols === this.length) {
|
|
352
|
+
return this._data.length * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength;
|
|
353
|
+
}
|
|
354
|
+
const uint32Cells = cols * CELL_SIZE;
|
|
355
|
+
if (cols > this.length) {
|
|
356
|
+
if (this._data.buffer.byteLength >= uint32Cells * 4) {
|
|
357
|
+
// optimization: avoid alloc and data copy if buffer has enough room
|
|
358
|
+
this._data = new Uint32Array(this._data.buffer, 0, uint32Cells);
|
|
359
|
+
} else {
|
|
360
|
+
// slow path: new alloc and full data copy
|
|
361
|
+
const data = new Uint32Array(uint32Cells);
|
|
362
|
+
data.set(this._data);
|
|
363
|
+
this._data = data;
|
|
364
|
+
}
|
|
365
|
+
for (let i = this.length; i < cols; ++i) {
|
|
366
|
+
this.setCell(i, fillCellData);
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
// optimization: just shrink the view on existing buffer
|
|
370
|
+
this._data = this._data.subarray(0, uint32Cells);
|
|
371
|
+
// Remove any cut off combined data
|
|
372
|
+
const keys = Object.keys(this._combined);
|
|
373
|
+
for (let i = 0; i < keys.length; i++) {
|
|
374
|
+
const key = parseInt(keys[i], 10);
|
|
375
|
+
if (key >= cols) {
|
|
376
|
+
delete this._combined[key];
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// remove any cut off extended attributes
|
|
380
|
+
const extKeys = Object.keys(this._extendedAttrs);
|
|
381
|
+
for (let i = 0; i < extKeys.length; i++) {
|
|
382
|
+
const key = parseInt(extKeys[i], 10);
|
|
383
|
+
if (key >= cols) {
|
|
384
|
+
delete this._extendedAttrs[key];
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
this.length = cols;
|
|
389
|
+
return uint32Cells * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Cleanup underlying array buffer.
|
|
394
|
+
* A cleanup will be triggered if the array buffer exceeds the actual used
|
|
395
|
+
* memory by a factor of CLEANUP_THRESHOLD.
|
|
396
|
+
* Returns 0 or 1 indicating whether a cleanup happened.
|
|
397
|
+
*/
|
|
398
|
+
public cleanupMemory(): number {
|
|
399
|
+
if (this._data.length * 4 * CLEANUP_THRESHOLD < this._data.buffer.byteLength) {
|
|
400
|
+
const data = new Uint32Array(this._data.length);
|
|
401
|
+
data.set(this._data);
|
|
402
|
+
this._data = data;
|
|
403
|
+
return 1;
|
|
404
|
+
}
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** fill a line with fillCharData */
|
|
409
|
+
public fill(fillCellData: ICellData, respectProtect: boolean = false): void {
|
|
410
|
+
// full branching on respectProtect==true, hopefully getting fast JIT for standard case
|
|
411
|
+
if (respectProtect) {
|
|
412
|
+
for (let i = 0; i < this.length; ++i) {
|
|
413
|
+
if (!this.isProtected(i)) {
|
|
414
|
+
this.setCell(i, fillCellData);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
this._combined = {};
|
|
420
|
+
this._extendedAttrs = {};
|
|
421
|
+
for (let i = 0; i < this.length; ++i) {
|
|
422
|
+
this.setCell(i, fillCellData);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** alter to a full copy of line */
|
|
427
|
+
public copyFrom(line: BufferLine): void {
|
|
428
|
+
if (this.length !== line.length) {
|
|
429
|
+
this._data = new Uint32Array(line._data);
|
|
430
|
+
} else {
|
|
431
|
+
// use high speed copy if lengths are equal
|
|
432
|
+
this._data.set(line._data);
|
|
433
|
+
}
|
|
434
|
+
this.length = line.length;
|
|
435
|
+
this._combined = {};
|
|
436
|
+
for (const el in line._combined) {
|
|
437
|
+
this._combined[el] = line._combined[el];
|
|
438
|
+
}
|
|
439
|
+
this._extendedAttrs = {};
|
|
440
|
+
for (const el in line._extendedAttrs) {
|
|
441
|
+
this._extendedAttrs[el] = line._extendedAttrs[el];
|
|
442
|
+
}
|
|
443
|
+
this.isWrapped = line.isWrapped;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** create a new clone */
|
|
447
|
+
public clone(): IBufferLine {
|
|
448
|
+
const newLine = new BufferLine(0);
|
|
449
|
+
newLine._data = new Uint32Array(this._data);
|
|
450
|
+
newLine.length = this.length;
|
|
451
|
+
for (const el in this._combined) {
|
|
452
|
+
newLine._combined[el] = this._combined[el];
|
|
453
|
+
}
|
|
454
|
+
for (const el in this._extendedAttrs) {
|
|
455
|
+
newLine._extendedAttrs[el] = this._extendedAttrs[el];
|
|
456
|
+
}
|
|
457
|
+
newLine.isWrapped = this.isWrapped;
|
|
458
|
+
return newLine;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
public getTrimmedLength(): number {
|
|
462
|
+
for (let i = this.length - 1; i >= 0; --i) {
|
|
463
|
+
if ((this._data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK)) {
|
|
464
|
+
return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
public getNoBgTrimmedLength(): number {
|
|
471
|
+
for (let i = this.length - 1; i >= 0; --i) {
|
|
472
|
+
if ((this._data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK) || (this._data[i * CELL_SIZE + Cell.BG] & Attributes.CM_MASK)) {
|
|
473
|
+
return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return 0;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
public copyCellsFrom(src: BufferLine, srcCol: number, destCol: number, length: number, applyInReverse: boolean): void {
|
|
480
|
+
const srcData = src._data;
|
|
481
|
+
if (applyInReverse) {
|
|
482
|
+
for (let cell = length - 1; cell >= 0; cell--) {
|
|
483
|
+
for (let i = 0; i < CELL_SIZE; i++) {
|
|
484
|
+
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i];
|
|
485
|
+
}
|
|
486
|
+
if (srcData[(srcCol + cell) * CELL_SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
|
487
|
+
this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
for (let cell = 0; cell < length; cell++) {
|
|
492
|
+
for (let i = 0; i < CELL_SIZE; i++) {
|
|
493
|
+
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i];
|
|
494
|
+
}
|
|
495
|
+
if (srcData[(srcCol + cell) * CELL_SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
|
|
496
|
+
this._extendedAttrs[destCol + cell] = src._extendedAttrs[srcCol + cell];
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Move any combined data over as needed, FIXME: repeat for extended attrs
|
|
502
|
+
const srcCombinedKeys = Object.keys(src._combined);
|
|
503
|
+
for (let i = 0; i < srcCombinedKeys.length; i++) {
|
|
504
|
+
const key = parseInt(srcCombinedKeys[i], 10);
|
|
505
|
+
if (key >= srcCol) {
|
|
506
|
+
this._combined[key - srcCol + destCol] = src._combined[key];
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = this.length): string {
|
|
512
|
+
if (trimRight) {
|
|
513
|
+
endCol = Math.min(endCol, this.getTrimmedLength());
|
|
514
|
+
}
|
|
515
|
+
let result = '';
|
|
516
|
+
while (startCol < endCol) {
|
|
517
|
+
const content = this._data[startCol * CELL_SIZE + Cell.CONTENT];
|
|
518
|
+
const cp = content & Content.CODEPOINT_MASK;
|
|
519
|
+
result += (content & Content.IS_COMBINED_MASK) ? this._combined[startCol] : (cp) ? stringFromCodePoint(cp) : WHITESPACE_CELL_CHAR;
|
|
520
|
+
startCol += (content >> Content.WIDTH_SHIFT) || 1; // always advance by 1
|
|
521
|
+
}
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2021 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IBufferRange } from '@xterm/xterm';
|
|
7
|
+
|
|
8
|
+
export function getRangeLength(range: IBufferRange, bufferCols: number): number {
|
|
9
|
+
if (range.start.y > range.end.y) {
|
|
10
|
+
throw new Error(`Buffer range end (${range.end.x}, ${range.end.y}) cannot be before start (${range.start.x}, ${range.start.y})`);
|
|
11
|
+
}
|
|
12
|
+
return bufferCols * (range.end.y - range.start.y) + (range.end.x - range.start.x + 1);
|
|
13
|
+
}
|