@xterm/xterm 5.6.0-beta.3 → 5.6.0-beta.30
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/README.md +4 -0
- package/css/xterm.css +6 -0
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/package.json +8 -11
- package/src/browser/AccessibilityManager.ts +38 -10
- package/src/browser/Viewport.ts +15 -2
- package/src/browser/public/Terminal.ts +6 -4
- package/src/browser/renderer/dom/DomRenderer.ts +3 -5
- package/src/browser/renderer/shared/CustomGlyphs.ts +6 -0
- package/src/browser/services/CoreBrowserService.ts +7 -3
- package/src/common/EventEmitter.ts +6 -11
- package/src/common/InputHandler.ts +10 -6
- package/src/common/SortedList.ts +86 -10
- package/src/common/buffer/Buffer.ts +1 -1
- package/src/common/services/DecorationService.ts +2 -1
- package/typings/xterm.d.ts +5 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/xterm",
|
|
3
3
|
"description": "Full xterm terminal, in your browser",
|
|
4
|
-
"version": "5.6.0-beta.
|
|
4
|
+
"version": "5.6.0-beta.30",
|
|
5
5
|
"main": "lib/xterm.js",
|
|
6
6
|
"style": "css/xterm.css",
|
|
7
7
|
"types": "typings/xterm.d.ts",
|
|
@@ -34,15 +34,11 @@
|
|
|
34
34
|
"lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
|
|
35
35
|
"test": "npm run test-unit",
|
|
36
36
|
"posttest": "npm run lint",
|
|
37
|
-
"test-
|
|
38
|
-
"test-
|
|
39
|
-
"test-
|
|
40
|
-
"test-
|
|
41
|
-
"test-
|
|
42
|
-
"test-playwright-chromium": "node ./bin/test_playwright.js --workers=75% \"--project=Chrome Stable\"",
|
|
43
|
-
"test-playwright-firefox": "node ./bin/test_playwright.js --workers=75% \"--project=Firefox Stable\"",
|
|
44
|
-
"test-playwright-webkit": "node ./bin/test_playwright.js --workers=75% \"--project=WebKit\"",
|
|
45
|
-
"test-playwright-debug": "node ./bin/test_playwright.js --workers=1 --headed --timeout=30000",
|
|
37
|
+
"test-integration": "node ./bin/test_playwright.js --workers=75%",
|
|
38
|
+
"test-integration-chromium": "node ./bin/test_playwright.js --workers=75% \"--project=ChromeStable\"",
|
|
39
|
+
"test-integration-firefox": "node ./bin/test_playwright.js --workers=75% \"--project=FirefoxStable\"",
|
|
40
|
+
"test-integration-webkit": "node ./bin/test_playwright.js --workers=75% \"--project=WebKit\"",
|
|
41
|
+
"test-integration-debug": "node ./bin/test_playwright.js --workers=1 --headed --timeout=30000",
|
|
46
42
|
"test-unit": "node ./bin/test.js",
|
|
47
43
|
"test-unit-coverage": "node ./bin/test.js --coverage",
|
|
48
44
|
"test-unit-dev": "cross-env NODE_PATH='./out' mocha",
|
|
@@ -61,6 +57,7 @@
|
|
|
61
57
|
"devDependencies": {
|
|
62
58
|
"@lunapaint/png-codec": "^0.2.0",
|
|
63
59
|
"@playwright/test": "^1.37.1",
|
|
60
|
+
"@stylistic/eslint-plugin": "^2.3.0",
|
|
64
61
|
"@types/chai": "^4.2.22",
|
|
65
62
|
"@types/debug": "^4.1.7",
|
|
66
63
|
"@types/deep-equal": "^1.0.1",
|
|
@@ -91,7 +88,7 @@
|
|
|
91
88
|
"source-map-loader": "^3.0.0",
|
|
92
89
|
"source-map-support": "^0.5.20",
|
|
93
90
|
"ts-loader": "^9.3.1",
|
|
94
|
-
"typescript": "
|
|
91
|
+
"typescript": "5.5",
|
|
95
92
|
"utf8": "^3.0.0",
|
|
96
93
|
"webpack": "^5.61.0",
|
|
97
94
|
"webpack-cli": "^4.9.1",
|
|
@@ -58,10 +58,11 @@ export class AccessibilityManager extends Disposable {
|
|
|
58
58
|
@IRenderService private readonly _renderService: IRenderService
|
|
59
59
|
) {
|
|
60
60
|
super();
|
|
61
|
-
|
|
61
|
+
const doc = this._coreBrowserService.mainDocument;
|
|
62
|
+
this._accessibilityContainer = doc.createElement('div');
|
|
62
63
|
this._accessibilityContainer.classList.add('xterm-accessibility');
|
|
63
64
|
|
|
64
|
-
this._rowContainer =
|
|
65
|
+
this._rowContainer = doc.createElement('div');
|
|
65
66
|
this._rowContainer.setAttribute('role', 'list');
|
|
66
67
|
this._rowContainer.classList.add('xterm-accessibility-tree');
|
|
67
68
|
this._rowElements = [];
|
|
@@ -75,10 +76,9 @@ export class AccessibilityManager extends Disposable {
|
|
|
75
76
|
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
|
|
76
77
|
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
|
|
77
78
|
|
|
78
|
-
this._refreshRowsDimensions();
|
|
79
79
|
this._accessibilityContainer.appendChild(this._rowContainer);
|
|
80
80
|
|
|
81
|
-
this._liveRegion =
|
|
81
|
+
this._liveRegion = doc.createElement('div');
|
|
82
82
|
this._liveRegion.classList.add('live-region');
|
|
83
83
|
this._liveRegion.setAttribute('aria-live', 'assertive');
|
|
84
84
|
this._accessibilityContainer.appendChild(this._liveRegion);
|
|
@@ -93,12 +93,12 @@ export class AccessibilityManager extends Disposable {
|
|
|
93
93
|
this._rowContainer.classList.add('debug');
|
|
94
94
|
|
|
95
95
|
// Use a `<div class="xterm">` container so that the css will still apply.
|
|
96
|
-
this._debugRootContainer =
|
|
96
|
+
this._debugRootContainer = doc.createElement('div');
|
|
97
97
|
this._debugRootContainer.classList.add('xterm');
|
|
98
98
|
|
|
99
|
-
this._debugRootContainer.appendChild(
|
|
99
|
+
this._debugRootContainer.appendChild(doc.createTextNode('------start a11y------'));
|
|
100
100
|
this._debugRootContainer.appendChild(this._accessibilityContainer);
|
|
101
|
-
this._debugRootContainer.appendChild(
|
|
101
|
+
this._debugRootContainer.appendChild(doc.createTextNode('------end a11y------'));
|
|
102
102
|
|
|
103
103
|
this._terminal.element.insertAdjacentElement('afterend', this._debugRootContainer);
|
|
104
104
|
} else {
|
|
@@ -115,9 +115,10 @@ export class AccessibilityManager extends Disposable {
|
|
|
115
115
|
this.register(this._terminal.onKey(e => this._handleKey(e.key)));
|
|
116
116
|
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
|
|
117
117
|
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
|
|
118
|
-
this.register(addDisposableDomListener(
|
|
118
|
+
this.register(addDisposableDomListener(doc, 'selectionchange', () => this._handleSelectionChange()));
|
|
119
119
|
this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions()));
|
|
120
120
|
|
|
121
|
+
this._refreshRowsDimensions();
|
|
121
122
|
this._refreshRows();
|
|
122
123
|
this.register(toDisposable(() => {
|
|
123
124
|
if (DEBUG) {
|
|
@@ -192,6 +193,7 @@ export class AccessibilityManager extends Disposable {
|
|
|
192
193
|
}
|
|
193
194
|
element.setAttribute('aria-posinset', posInSet);
|
|
194
195
|
element.setAttribute('aria-setsize', setSize);
|
|
196
|
+
this._alignRowWidth(element);
|
|
195
197
|
}
|
|
196
198
|
}
|
|
197
199
|
this._announceCharacters();
|
|
@@ -270,7 +272,7 @@ export class AccessibilityManager extends Disposable {
|
|
|
270
272
|
return;
|
|
271
273
|
}
|
|
272
274
|
|
|
273
|
-
const selection =
|
|
275
|
+
const selection = this._coreBrowserService.mainDocument.getSelection();
|
|
274
276
|
if (!selection) {
|
|
275
277
|
return;
|
|
276
278
|
}
|
|
@@ -389,19 +391,45 @@ export class AccessibilityManager extends Disposable {
|
|
|
389
391
|
this._refreshRowDimensions(element);
|
|
390
392
|
return element;
|
|
391
393
|
}
|
|
394
|
+
|
|
392
395
|
private _refreshRowsDimensions(): void {
|
|
393
396
|
if (!this._renderService.dimensions.css.cell.height) {
|
|
394
397
|
return;
|
|
395
398
|
}
|
|
396
|
-
this._accessibilityContainer.style
|
|
399
|
+
Object.assign(this._accessibilityContainer.style, {
|
|
400
|
+
width: `${this._renderService.dimensions.css.canvas.width}px`,
|
|
401
|
+
fontSize: `${this._terminal.options.fontSize}px`
|
|
402
|
+
});
|
|
397
403
|
if (this._rowElements.length !== this._terminal.rows) {
|
|
398
404
|
this._handleResize(this._terminal.rows);
|
|
399
405
|
}
|
|
400
406
|
for (let i = 0; i < this._terminal.rows; i++) {
|
|
401
407
|
this._refreshRowDimensions(this._rowElements[i]);
|
|
408
|
+
this._alignRowWidth(this._rowElements[i]);
|
|
402
409
|
}
|
|
403
410
|
}
|
|
411
|
+
|
|
404
412
|
private _refreshRowDimensions(element: HTMLElement): void {
|
|
405
413
|
element.style.height = `${this._renderService.dimensions.css.cell.height}px`;
|
|
406
414
|
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Scale the width of a row so that each of the character is (mostly) aligned
|
|
418
|
+
* with the actual rendering. This will allow the screen reader to draw
|
|
419
|
+
* selection outline at the correct position.
|
|
420
|
+
*
|
|
421
|
+
* On top of using the "monospace" font and correct font size, the scaling
|
|
422
|
+
* here is necessary to handle characters that are not covered by the font
|
|
423
|
+
* (e.g. CJK).
|
|
424
|
+
*/
|
|
425
|
+
private _alignRowWidth(element: HTMLElement): void {
|
|
426
|
+
element.style.transform = '';
|
|
427
|
+
const width = element.getBoundingClientRect().width;
|
|
428
|
+
const lastColumn = this._rowColumns.get(element)?.slice(-1)?.[0];
|
|
429
|
+
if (!lastColumn) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const targetWidth = lastColumn * this._renderService.dimensions.css.cell.width;
|
|
433
|
+
element.style.transform = `scaleX(${targetWidth / width})`;
|
|
434
|
+
}
|
|
407
435
|
}
|
package/src/browser/Viewport.ts
CHANGED
|
@@ -36,6 +36,8 @@ export class Viewport extends Disposable implements IViewport {
|
|
|
36
36
|
private _activeBuffer: IBuffer;
|
|
37
37
|
private _renderDimensions: IRenderDimensions;
|
|
38
38
|
|
|
39
|
+
private _smoothScrollAnimationFrame: number = 0;
|
|
40
|
+
|
|
39
41
|
// Stores a partial line amount when scrolling, this is used to keep track of how much of a line
|
|
40
42
|
// is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
|
|
41
43
|
// quick fix and could have a more robust solution in place that reset the value when needed.
|
|
@@ -49,6 +51,8 @@ export class Viewport extends Disposable implements IViewport {
|
|
|
49
51
|
target: -1
|
|
50
52
|
};
|
|
51
53
|
|
|
54
|
+
private _ensureTimeout: number;
|
|
55
|
+
|
|
52
56
|
private readonly _onRequestScrollLines = this.register(new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>());
|
|
53
57
|
public readonly onRequestScrollLines = this._onRequestScrollLines.event;
|
|
54
58
|
|
|
@@ -81,7 +85,7 @@ export class Viewport extends Disposable implements IViewport {
|
|
|
81
85
|
this.register(this._optionsService.onSpecificOptionChange('scrollback', () => this.syncScrollArea()));
|
|
82
86
|
|
|
83
87
|
// Perform this async to ensure the ICharSizeService is ready.
|
|
84
|
-
setTimeout(() => this.syncScrollArea());
|
|
88
|
+
this._ensureTimeout = window.setTimeout(() => this.syncScrollArea());
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
private _handleThemeChange(colors: ReadonlyColorSet): void {
|
|
@@ -211,7 +215,12 @@ export class Viewport extends Disposable implements IViewport {
|
|
|
211
215
|
|
|
212
216
|
// Continue or finish smooth scroll
|
|
213
217
|
if (percent < 1) {
|
|
214
|
-
this.
|
|
218
|
+
if (!this._smoothScrollAnimationFrame) {
|
|
219
|
+
this._smoothScrollAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
|
220
|
+
this._smoothScrollAnimationFrame = 0;
|
|
221
|
+
this._smoothScroll();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
215
224
|
} else {
|
|
216
225
|
this._clearSmoothScrollState();
|
|
217
226
|
}
|
|
@@ -398,4 +407,8 @@ export class Viewport extends Disposable implements IViewport {
|
|
|
398
407
|
this._viewportElement.scrollTop += deltaY;
|
|
399
408
|
return this._bubbleScroll(ev, deltaY);
|
|
400
409
|
}
|
|
410
|
+
|
|
411
|
+
public dispose(): void {
|
|
412
|
+
clearTimeout(this._ensureTimeout);
|
|
413
|
+
}
|
|
401
414
|
}
|
|
@@ -20,6 +20,8 @@ import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOption
|
|
|
20
20
|
*/
|
|
21
21
|
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
|
|
22
22
|
|
|
23
|
+
let $value = 0;
|
|
24
|
+
|
|
23
25
|
export class Terminal extends Disposable implements ITerminalApi {
|
|
24
26
|
private _core: ITerminal;
|
|
25
27
|
private _addonManager: AddonManager;
|
|
@@ -249,16 +251,16 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
private _verifyIntegers(...values: number[]): void {
|
|
252
|
-
for (
|
|
253
|
-
if (value === Infinity || isNaN(value) || value % 1 !== 0) {
|
|
254
|
+
for ($value of values) {
|
|
255
|
+
if ($value === Infinity || isNaN($value) || $value % 1 !== 0) {
|
|
254
256
|
throw new Error('This API only accepts integers');
|
|
255
257
|
}
|
|
256
258
|
}
|
|
257
259
|
}
|
|
258
260
|
|
|
259
261
|
private _verifyPositiveIntegers(...values: number[]): void {
|
|
260
|
-
for (
|
|
261
|
-
if (value && (value === Infinity || isNaN(value) || value % 1 !== 0 || value < 0)) {
|
|
262
|
+
for ($value of values) {
|
|
263
|
+
if ($value && ($value === Infinity || isNaN($value) || $value % 1 !== 0 || $value < 0)) {
|
|
262
264
|
throw new Error('This API only accepts positive integers');
|
|
263
265
|
}
|
|
264
266
|
}
|
|
@@ -343,6 +343,9 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
|
|
346
|
+
if (!this._selectionRenderModel.hasSelection) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
346
349
|
|
|
347
350
|
// Translate from buffer position to viewport position
|
|
348
351
|
const viewportStartRow = this._selectionRenderModel.viewportStartRow;
|
|
@@ -350,11 +353,6 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
350
353
|
const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
|
|
351
354
|
const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
|
|
352
355
|
|
|
353
|
-
// No need to draw the selection
|
|
354
|
-
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
356
|
// Create the selections
|
|
359
357
|
const documentFragment = this._document.createDocumentFragment();
|
|
360
358
|
|
|
@@ -355,6 +355,12 @@ const enum VectorType {
|
|
|
355
355
|
* Original symbols defined in https://github.com/powerline/fontpatcher
|
|
356
356
|
*/
|
|
357
357
|
export const powerlineDefinitions: { [index: string]: IVectorShape } = {
|
|
358
|
+
// Git branch
|
|
359
|
+
'\u{E0A0}': { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: VectorType.FILL },
|
|
360
|
+
// L N
|
|
361
|
+
'\u{E0A1}': { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.355,.03 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: VectorType.FILL },
|
|
362
|
+
// Lock
|
|
363
|
+
'\u{E0A2}': { d: 'M.25,.94 C.16,.94,.11,.92,.11,.87 L.11,.53 C.11,.48,.15,.455,.23,.45 L.23,.3 C.23,.25,.26,.22,.31,.19 C.36,.16,.43,.15,.51,.15 C.59,.15,.66,.16,.71,.19 C.77,.22,.79,.26,.79,.3 L.79,.45 C.87,.45,.91,.48,.91,.53 L.91,.87 C.91,.92,.86,.94,.77,.94 L.24,.94 M.53,.2 C.49,.2,.45,.21,.42,.23 C.39,.25,.38,.27,.38,.3 L.38,.45 L.68,.45 L.68,.3 C.68,.27,.67,.25,.64,.23 C.61,.21,.58,.2,.53,.2 M.58,.82 L.58,.66 C.63,.65,.65,.63,.65,.6 C.65,.58,.64,.57,.61,.56 C.58,.55,.56,.54,.52,.54 C.48,.54,.46,.55,.43,.56 C.4,.57,.39,.59,.39,.6 C.39,.63,.41,.64,.46,.66 L.46,.82 L.57,.82', type: VectorType.FILL },
|
|
358
364
|
// Right triangle solid
|
|
359
365
|
'\u{E0B0}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.FILL, rightPadding: 2 },
|
|
360
366
|
// Right triangle line
|
|
@@ -13,7 +13,7 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic
|
|
|
13
13
|
|
|
14
14
|
private _isFocused = false;
|
|
15
15
|
private _cachedIsFocused: boolean | undefined = undefined;
|
|
16
|
-
private _screenDprMonitor = new ScreenDprMonitor(this._window);
|
|
16
|
+
private _screenDprMonitor = this.register(new ScreenDprMonitor(this._window));
|
|
17
17
|
|
|
18
18
|
private readonly _onDprChange = this.register(new EventEmitter<number>());
|
|
19
19
|
public readonly onDprChange = this._onDprChange.event;
|
|
@@ -31,8 +31,12 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic
|
|
|
31
31
|
this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w)));
|
|
32
32
|
this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange));
|
|
33
33
|
|
|
34
|
-
this.
|
|
35
|
-
|
|
34
|
+
this.register(
|
|
35
|
+
addDisposableDomListener(this._textarea, 'focus', () => (this._isFocused = true))
|
|
36
|
+
);
|
|
37
|
+
this.register(
|
|
38
|
+
addDisposableDomListener(this._textarea, 'blur', () => (this._isFocused = false))
|
|
39
|
+
);
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
public get window(): Window & typeof globalThis {
|
|
@@ -20,23 +20,18 @@ export interface IEventEmitter<T, U = void> {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
|
|
23
|
-
private _listeners: IListener<T, U
|
|
23
|
+
private _listeners: Set<IListener<T, U>> = new Set();
|
|
24
24
|
private _event?: IEvent<T, U>;
|
|
25
25
|
private _disposed: boolean = false;
|
|
26
26
|
|
|
27
27
|
public get event(): IEvent<T, U> {
|
|
28
28
|
if (!this._event) {
|
|
29
29
|
this._event = (listener: (arg1: T, arg2: U) => any) => {
|
|
30
|
-
this._listeners.
|
|
30
|
+
this._listeners.add(listener);
|
|
31
31
|
const disposable = {
|
|
32
32
|
dispose: () => {
|
|
33
33
|
if (!this._disposed) {
|
|
34
|
-
|
|
35
|
-
if (this._listeners[i] === listener) {
|
|
36
|
-
this._listeners.splice(i, 1);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
34
|
+
this._listeners.delete(listener);
|
|
40
35
|
}
|
|
41
36
|
}
|
|
42
37
|
};
|
|
@@ -48,8 +43,8 @@ export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
|
|
|
48
43
|
|
|
49
44
|
public fire(arg1: T, arg2: U): void {
|
|
50
45
|
const queue: IListener<T, U>[] = [];
|
|
51
|
-
for (
|
|
52
|
-
queue.push(
|
|
46
|
+
for (const l of this._listeners.values()) {
|
|
47
|
+
queue.push(l);
|
|
53
48
|
}
|
|
54
49
|
for (let i = 0; i < queue.length; i++) {
|
|
55
50
|
queue[i].call(undefined, arg1, arg2);
|
|
@@ -63,7 +58,7 @@ export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
|
|
|
63
58
|
|
|
64
59
|
public clearListeners(): void {
|
|
65
60
|
if (this._listeners) {
|
|
66
|
-
this._listeners.
|
|
61
|
+
this._listeners.clear();
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
}
|
|
@@ -2972,14 +2972,18 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
2972
2972
|
* feedback. Use `OSC 8 ; ; BEL` to finish the current hyperlink.
|
|
2973
2973
|
*/
|
|
2974
2974
|
public setHyperlink(data: string): boolean {
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2975
|
+
// Arg parsing is special cases to support unencoded semi-colons in the URIs (#4944)
|
|
2976
|
+
const idx = data.indexOf(';');
|
|
2977
|
+
if (idx === -1) {
|
|
2978
|
+
// malformed sequence, just return as handled
|
|
2979
|
+
return true;
|
|
2978
2980
|
}
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
+
const id = data.slice(0, idx).trim();
|
|
2982
|
+
const uri = data.slice(idx + 1);
|
|
2983
|
+
if (uri) {
|
|
2984
|
+
return this._createHyperlink(id, uri);
|
|
2981
2985
|
}
|
|
2982
|
-
if (
|
|
2986
|
+
if (id.trim()) {
|
|
2983
2987
|
return false;
|
|
2984
2988
|
}
|
|
2985
2989
|
return this._finishHyperlink();
|
package/src/common/SortedList.ts
CHANGED
|
@@ -3,16 +3,27 @@
|
|
|
3
3
|
* @license MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { IdleTaskQueue } from 'common/TaskQueue';
|
|
7
|
+
|
|
6
8
|
// Work variables to avoid garbage collection.
|
|
7
9
|
let i = 0;
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
|
-
* A generic list that is maintained in sorted order and allows values with duplicate keys.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* A generic list that is maintained in sorted order and allows values with duplicate keys. Deferred
|
|
13
|
+
* batch insertion and deletion is used to significantly reduce the time it takes to insert and
|
|
14
|
+
* delete a large amount of items in succession. This list is based on binary search and as such
|
|
15
|
+
* locating a key will take O(log n) amortized, this includes the by key iterator.
|
|
13
16
|
*/
|
|
14
17
|
export class SortedList<T> {
|
|
15
|
-
private
|
|
18
|
+
private _array: T[] = [];
|
|
19
|
+
|
|
20
|
+
private readonly _insertedValues: T[] = [];
|
|
21
|
+
private readonly _flushInsertedTask = new IdleTaskQueue();
|
|
22
|
+
private _isFlushingInserted = false;
|
|
23
|
+
|
|
24
|
+
private readonly _deletedIndices: number[] = [];
|
|
25
|
+
private readonly _flushDeletedTask = new IdleTaskQueue();
|
|
26
|
+
private _isFlushingDeleted = false;
|
|
16
27
|
|
|
17
28
|
constructor(
|
|
18
29
|
private readonly _getKey: (value: T) => number
|
|
@@ -21,18 +32,50 @@ export class SortedList<T> {
|
|
|
21
32
|
|
|
22
33
|
public clear(): void {
|
|
23
34
|
this._array.length = 0;
|
|
35
|
+
this._insertedValues.length = 0;
|
|
36
|
+
this._flushInsertedTask.clear();
|
|
37
|
+
this._isFlushingInserted = false;
|
|
38
|
+
this._deletedIndices.length = 0;
|
|
39
|
+
this._flushDeletedTask.clear();
|
|
40
|
+
this._isFlushingDeleted = false;
|
|
24
41
|
}
|
|
25
42
|
|
|
26
43
|
public insert(value: T): void {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
this._flushCleanupDeleted();
|
|
45
|
+
if (this._insertedValues.length === 0) {
|
|
46
|
+
this._flushInsertedTask.enqueue(() => this._flushInserted());
|
|
47
|
+
}
|
|
48
|
+
this._insertedValues.push(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private _flushInserted(): void {
|
|
52
|
+
const sortedAddedValues = this._insertedValues.sort((a, b) => this._getKey(a) - this._getKey(b));
|
|
53
|
+
let sortedAddedValuesIndex = 0;
|
|
54
|
+
let arrayIndex = 0;
|
|
55
|
+
|
|
56
|
+
const newArray = new Array(this._array.length + this._insertedValues.length);
|
|
57
|
+
|
|
58
|
+
for (let newArrayIndex = 0; newArrayIndex < newArray.length; newArrayIndex++) {
|
|
59
|
+
if (arrayIndex >= this._array.length || this._getKey(sortedAddedValues[sortedAddedValuesIndex]) <= this._getKey(this._array[arrayIndex])) {
|
|
60
|
+
newArray[newArrayIndex] = sortedAddedValues[sortedAddedValuesIndex];
|
|
61
|
+
sortedAddedValuesIndex++;
|
|
62
|
+
} else {
|
|
63
|
+
newArray[newArrayIndex] = this._array[arrayIndex++];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this._array = newArray;
|
|
68
|
+
this._insertedValues.length = 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private _flushCleanupInserted(): void {
|
|
72
|
+
if (!this._isFlushingInserted && this._insertedValues.length > 0) {
|
|
73
|
+
this._flushInsertedTask.flush();
|
|
30
74
|
}
|
|
31
|
-
i = this._search(this._getKey(value));
|
|
32
|
-
this._array.splice(i, 0, value);
|
|
33
75
|
}
|
|
34
76
|
|
|
35
77
|
public delete(value: T): boolean {
|
|
78
|
+
this._flushCleanupInserted();
|
|
36
79
|
if (this._array.length === 0) {
|
|
37
80
|
return false;
|
|
38
81
|
}
|
|
@@ -49,14 +92,43 @@ export class SortedList<T> {
|
|
|
49
92
|
}
|
|
50
93
|
do {
|
|
51
94
|
if (this._array[i] === value) {
|
|
52
|
-
this.
|
|
95
|
+
if (this._deletedIndices.length === 0) {
|
|
96
|
+
this._flushDeletedTask.enqueue(() => this._flushDeleted());
|
|
97
|
+
}
|
|
98
|
+
this._deletedIndices.push(i);
|
|
53
99
|
return true;
|
|
54
100
|
}
|
|
55
101
|
} while (++i < this._array.length && this._getKey(this._array[i]) === key);
|
|
56
102
|
return false;
|
|
57
103
|
}
|
|
58
104
|
|
|
105
|
+
private _flushDeleted(): void {
|
|
106
|
+
this._isFlushingDeleted = true;
|
|
107
|
+
const sortedDeletedIndices = this._deletedIndices.sort((a, b) => a - b);
|
|
108
|
+
let sortedDeletedIndicesIndex = 0;
|
|
109
|
+
const newArray = new Array(this._array.length - sortedDeletedIndices.length);
|
|
110
|
+
let newArrayIndex = 0;
|
|
111
|
+
for (let i = 0; i < this._array.length; i++) {
|
|
112
|
+
if (sortedDeletedIndices[sortedDeletedIndicesIndex] === i) {
|
|
113
|
+
sortedDeletedIndicesIndex++;
|
|
114
|
+
} else {
|
|
115
|
+
newArray[newArrayIndex++] = this._array[i];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this._array = newArray;
|
|
119
|
+
this._deletedIndices.length = 0;
|
|
120
|
+
this._isFlushingDeleted = false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private _flushCleanupDeleted(): void {
|
|
124
|
+
if (!this._isFlushingDeleted && this._deletedIndices.length > 0) {
|
|
125
|
+
this._flushDeletedTask.flush();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
59
129
|
public *getKeyIterator(key: number): IterableIterator<T> {
|
|
130
|
+
this._flushCleanupInserted();
|
|
131
|
+
this._flushCleanupDeleted();
|
|
60
132
|
if (this._array.length === 0) {
|
|
61
133
|
return;
|
|
62
134
|
}
|
|
@@ -73,6 +145,8 @@ export class SortedList<T> {
|
|
|
73
145
|
}
|
|
74
146
|
|
|
75
147
|
public forEachByKey(key: number, callback: (value: T) => void): void {
|
|
148
|
+
this._flushCleanupInserted();
|
|
149
|
+
this._flushCleanupDeleted();
|
|
76
150
|
if (this._array.length === 0) {
|
|
77
151
|
return;
|
|
78
152
|
}
|
|
@@ -89,6 +163,8 @@ export class SortedList<T> {
|
|
|
89
163
|
}
|
|
90
164
|
|
|
91
165
|
public values(): IterableIterator<T> {
|
|
166
|
+
this._flushCleanupInserted();
|
|
167
|
+
this._flushCleanupDeleted();
|
|
92
168
|
// Duplicate the array to avoid issues when _array changes while iterating
|
|
93
169
|
return [...this._array].values();
|
|
94
170
|
}
|
|
@@ -611,8 +611,8 @@ export class Buffer implements IBuffer {
|
|
|
611
611
|
this._isClearing = true;
|
|
612
612
|
for (let i = 0; i < this.markers.length; i++) {
|
|
613
613
|
this.markers[i].dispose();
|
|
614
|
-
this.markers.splice(i--, 1);
|
|
615
614
|
}
|
|
615
|
+
this.markers.length = 0;
|
|
616
616
|
this._isClearing = false;
|
|
617
617
|
}
|
|
618
618
|
|
|
@@ -45,7 +45,8 @@ export class DecorationService extends Disposable implements IDecorationService
|
|
|
45
45
|
const decoration = new Decoration(options);
|
|
46
46
|
if (decoration) {
|
|
47
47
|
const markerDispose = decoration.marker.onDispose(() => decoration.dispose());
|
|
48
|
-
decoration.onDispose(() => {
|
|
48
|
+
const listener = decoration.onDispose(() => {
|
|
49
|
+
listener.dispose();
|
|
49
50
|
if (decoration) {
|
|
50
51
|
if (this._decorations.delete(decoration)) {
|
|
51
52
|
this._onDecorationRemoved.fire(decoration);
|
package/typings/xterm.d.ts
CHANGED
|
@@ -47,11 +47,13 @@ declare module '@xterm/xterm' {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* When enabled the cursor will be set to the beginning of the next line
|
|
50
|
-
* with every new line. This is equivalent to sending
|
|
51
|
-
* Normally the
|
|
52
|
-
* translation of
|
|
50
|
+
* with every new line. This is equivalent to sending `\r\n` for each `\n`.
|
|
51
|
+
* Normally the settings of the underlying PTY (`termios`) deal with the
|
|
52
|
+
* translation of `\n` to `\r\n` and this setting should not be used. If you
|
|
53
53
|
* deal with data from a non-PTY related source, this settings might be
|
|
54
54
|
* useful.
|
|
55
|
+
*
|
|
56
|
+
* @see https://pubs.opengroup.org/onlinepubs/007904975/basedefs/termios.h.html
|
|
55
57
|
*/
|
|
56
58
|
convertEol?: boolean;
|
|
57
59
|
|