@xterm/xterm 5.6.0-beta.1 → 5.6.0-beta.11

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/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.1",
4
+ "version": "5.6.0-beta.11",
5
5
  "main": "lib/xterm.js",
6
6
  "style": "css/xterm.css",
7
7
  "types": "typings/xterm.d.ts",
@@ -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._coreBrowserService.window.requestAnimationFrame(() => this._smoothScroll());
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 (const value of values) {
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 (const value of values) {
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
 
@@ -2979,7 +2979,7 @@ export class InputHandler extends Disposable implements IInputHandler {
2979
2979
  if (args[1]) {
2980
2980
  return this._createHyperlink(args[0], args[1]);
2981
2981
  }
2982
- if (args[0]) {
2982
+ if (args[0].trim()) {
2983
2983
  return false;
2984
2984
  }
2985
2985
  return this._finishHyperlink();
@@ -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. This
11
- * list is based on binary search and as such locating a key will take O(log n) amortized, this
12
- * includes the by key iterator.
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 readonly _array: T[] = [];
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
- if (this._array.length === 0) {
28
- this._array.push(value);
29
- return;
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._array.splice(i, 1);
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
  }
@@ -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);
@@ -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 '\r\n' for each '\n'.
51
- * Normally the termios settings of the underlying PTY deals with the
52
- * translation of '\n' to '\r\n' and this setting should not be used. If you
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