@xterm/xterm 6.1.0-beta.19 → 6.1.0-beta.190
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 +60 -38
- package/css/xterm.css +29 -22
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +8 -34
- package/lib/xterm.mjs.map +4 -4
- package/package.json +25 -14
- package/src/browser/AccessibilityManager.ts +6 -3
- package/src/browser/Clipboard.ts +6 -3
- package/src/browser/CoreBrowserTerminal.ts +147 -318
- package/src/browser/Dom.ts +178 -0
- package/src/browser/Linkifier.ts +11 -11
- package/src/browser/OscLinkProvider.ts +3 -1
- package/src/browser/RenderDebouncer.ts +2 -2
- package/src/browser/TimeBasedDebouncer.ts +2 -2
- package/src/browser/Types.ts +12 -11
- package/src/browser/Viewport.ts +37 -20
- package/src/browser/decorations/BufferDecorationRenderer.ts +1 -1
- package/src/browser/decorations/OverviewRulerRenderer.ts +15 -16
- package/src/browser/input/CompositionHelper.ts +44 -8
- package/src/browser/public/Terminal.ts +25 -28
- package/src/browser/renderer/dom/DomRenderer.ts +128 -8
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +19 -13
- package/src/browser/renderer/dom/WidthCache.ts +54 -52
- package/src/browser/renderer/shared/Constants.ts +7 -0
- package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
- package/src/browser/renderer/shared/Types.ts +8 -2
- package/src/browser/scrollable/abstractScrollbar.ts +300 -0
- package/src/browser/scrollable/fastDomNode.ts +126 -0
- package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
- package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
- package/src/browser/scrollable/mouseEvent.ts +292 -0
- package/src/browser/scrollable/scrollable.ts +486 -0
- package/src/browser/scrollable/scrollableElement.ts +579 -0
- package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
- package/src/browser/scrollable/scrollbarArrow.ts +110 -0
- package/src/browser/scrollable/scrollbarState.ts +246 -0
- package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
- package/src/browser/scrollable/touch.ts +485 -0
- package/src/browser/scrollable/verticalScrollbar.ts +143 -0
- package/src/browser/scrollable/widget.ts +23 -0
- package/src/browser/services/CharSizeService.ts +2 -2
- package/src/browser/services/CoreBrowserService.ts +7 -5
- package/src/browser/services/KeyboardService.ts +67 -0
- package/src/browser/services/LinkProviderService.ts +1 -1
- package/src/browser/services/MouseCoordsService.ts +47 -0
- package/src/browser/services/MouseService.ts +518 -25
- package/src/browser/services/RenderService.ts +22 -15
- package/src/browser/services/SelectionService.ts +16 -8
- package/src/browser/services/Services.ts +40 -17
- package/src/browser/services/ThemeService.ts +2 -2
- package/src/common/Async.ts +105 -0
- package/src/common/CircularList.ts +2 -2
- package/src/common/Color.ts +8 -0
- package/src/common/CoreTerminal.ts +28 -18
- package/src/common/Event.ts +118 -0
- package/src/common/InputHandler.ts +256 -36
- package/src/common/Lifecycle.ts +113 -0
- package/src/common/Platform.ts +13 -3
- package/src/common/SortedList.ts +7 -3
- package/src/common/TaskQueue.ts +9 -3
- package/src/common/Types.ts +35 -15
- package/src/common/Version.ts +9 -0
- package/src/common/buffer/Buffer.ts +20 -14
- package/src/common/buffer/BufferLine.ts +4 -5
- package/src/common/buffer/BufferSet.ts +7 -6
- package/src/common/buffer/CellData.ts +57 -0
- package/src/common/buffer/Marker.ts +2 -2
- package/src/common/buffer/Types.ts +6 -2
- package/src/common/data/EscapeSequences.ts +71 -70
- package/src/common/input/Keyboard.ts +14 -7
- package/src/common/input/KittyKeyboard.ts +496 -0
- package/src/common/input/Win32InputMode.ts +297 -0
- package/src/common/input/WriteBuffer.ts +34 -2
- package/src/common/input/XParseColor.ts +2 -2
- package/src/common/parser/ApcParser.ts +245 -0
- package/src/common/parser/Constants.ts +22 -4
- package/src/common/parser/DcsParser.ts +5 -5
- package/src/common/parser/EscapeSequenceParser.ts +75 -22
- package/src/common/parser/OscParser.ts +5 -5
- package/src/common/parser/Types.ts +34 -1
- package/src/common/public/BufferLineApiView.ts +2 -2
- package/src/common/public/BufferNamespaceApi.ts +2 -2
- package/src/common/public/ParserApi.ts +3 -0
- package/src/common/services/BufferService.ts +8 -5
- package/src/common/services/CharsetService.ts +4 -0
- package/src/common/services/CoreService.ts +18 -4
- package/src/common/services/DecorationService.ts +24 -8
- package/src/common/services/LogService.ts +1 -31
- package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
- package/src/common/services/OptionsService.ts +13 -4
- package/src/common/services/Services.ts +47 -40
- package/src/common/services/UnicodeService.ts +1 -1
- package/typings/xterm.d.ts +319 -35
- package/src/common/TypedArrayUtils.ts +0 -17
- package/src/vs/base/browser/browser.ts +0 -141
- package/src/vs/base/browser/canIUse.ts +0 -49
- package/src/vs/base/browser/dom.ts +0 -2369
- package/src/vs/base/browser/fastDomNode.ts +0 -316
- package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
- package/src/vs/base/browser/iframe.ts +0 -135
- package/src/vs/base/browser/keyboardEvent.ts +0 -213
- package/src/vs/base/browser/mouseEvent.ts +0 -229
- package/src/vs/base/browser/touch.ts +0 -372
- package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
- package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
- package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
- package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
- package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
- package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
- package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
- package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
- package/src/vs/base/browser/ui/widget.ts +0 -57
- package/src/vs/base/browser/window.ts +0 -14
- package/src/vs/base/common/arrays.ts +0 -887
- package/src/vs/base/common/arraysFind.ts +0 -202
- package/src/vs/base/common/assert.ts +0 -71
- package/src/vs/base/common/async.ts +0 -1992
- package/src/vs/base/common/cancellation.ts +0 -148
- package/src/vs/base/common/charCode.ts +0 -450
- package/src/vs/base/common/collections.ts +0 -140
- package/src/vs/base/common/decorators.ts +0 -130
- package/src/vs/base/common/equals.ts +0 -146
- package/src/vs/base/common/errors.ts +0 -303
- package/src/vs/base/common/event.ts +0 -1778
- package/src/vs/base/common/functional.ts +0 -32
- package/src/vs/base/common/hash.ts +0 -316
- package/src/vs/base/common/iterator.ts +0 -159
- package/src/vs/base/common/keyCodes.ts +0 -526
- package/src/vs/base/common/keybindings.ts +0 -284
- package/src/vs/base/common/lazy.ts +0 -47
- package/src/vs/base/common/lifecycle.ts +0 -801
- package/src/vs/base/common/linkedList.ts +0 -142
- package/src/vs/base/common/map.ts +0 -202
- package/src/vs/base/common/numbers.ts +0 -98
- package/src/vs/base/common/observable.ts +0 -76
- package/src/vs/base/common/observableInternal/api.ts +0 -31
- package/src/vs/base/common/observableInternal/autorun.ts +0 -281
- package/src/vs/base/common/observableInternal/base.ts +0 -489
- package/src/vs/base/common/observableInternal/debugName.ts +0 -145
- package/src/vs/base/common/observableInternal/derived.ts +0 -428
- package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
- package/src/vs/base/common/observableInternal/logging.ts +0 -328
- package/src/vs/base/common/observableInternal/promise.ts +0 -209
- package/src/vs/base/common/observableInternal/utils.ts +0 -610
- package/src/vs/base/common/platform.ts +0 -281
- package/src/vs/base/common/scrollable.ts +0 -522
- package/src/vs/base/common/sequence.ts +0 -34
- package/src/vs/base/common/stopwatch.ts +0 -43
- package/src/vs/base/common/strings.ts +0 -557
- package/src/vs/base/common/symbols.ts +0 -9
- package/src/vs/base/common/uint.ts +0 -59
- package/src/vs/patches/nls.ts +0 -90
- package/src/vs/typings/base-common.d.ts +0 -20
- package/src/vs/typings/require.d.ts +0 -42
- package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
- package/src/vs/typings/vscode-globals-product.d.ts +0 -33
|
@@ -30,6 +30,12 @@ export class CompositionHelper {
|
|
|
30
30
|
*/
|
|
31
31
|
private _compositionPosition: IPosition;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Text that existed after the composing range when composition started.
|
|
35
|
+
* This is used to avoid treating existing trailing text as new input.
|
|
36
|
+
*/
|
|
37
|
+
private _compositionSuffix: string;
|
|
38
|
+
|
|
33
39
|
/**
|
|
34
40
|
* Whether a composition is in the process of being sent, setting this to false will cancel any
|
|
35
41
|
* in-progress composition.
|
|
@@ -41,6 +47,11 @@ export class CompositionHelper {
|
|
|
41
47
|
*/
|
|
42
48
|
private _dataAlreadySent: string;
|
|
43
49
|
|
|
50
|
+
/**
|
|
51
|
+
* The pending textarea change timer, if any.
|
|
52
|
+
*/
|
|
53
|
+
private _textareaChangeTimer?: number;
|
|
54
|
+
|
|
44
55
|
constructor(
|
|
45
56
|
private readonly _textarea: HTMLTextAreaElement,
|
|
46
57
|
private readonly _compositionView: HTMLElement,
|
|
@@ -52,6 +63,7 @@ export class CompositionHelper {
|
|
|
52
63
|
this._isComposing = false;
|
|
53
64
|
this._isSendingComposition = false;
|
|
54
65
|
this._compositionPosition = { start: 0, end: 0 };
|
|
66
|
+
this._compositionSuffix = '';
|
|
55
67
|
this._dataAlreadySent = '';
|
|
56
68
|
}
|
|
57
69
|
|
|
@@ -60,7 +72,13 @@ export class CompositionHelper {
|
|
|
60
72
|
*/
|
|
61
73
|
public compositionstart(): void {
|
|
62
74
|
this._isComposing = true;
|
|
63
|
-
|
|
75
|
+
// It's important to use the selection here instead of textarea length to avoid conflicts with
|
|
76
|
+
// screen reader mode
|
|
77
|
+
const start = this._textarea.selectionStart ?? this._textarea.value.length;
|
|
78
|
+
const end = this._textarea.selectionEnd ?? start;
|
|
79
|
+
this._compositionPosition.start = Math.min(start, end);
|
|
80
|
+
this._compositionPosition.end = Math.max(start, end);
|
|
81
|
+
this._compositionSuffix = this._textarea.value.substring(this._compositionPosition.end);
|
|
64
82
|
this._compositionView.textContent = '';
|
|
65
83
|
this._dataAlreadySent = '';
|
|
66
84
|
this._compositionView.classList.add('active');
|
|
@@ -71,10 +89,13 @@ export class CompositionHelper {
|
|
|
71
89
|
* @param ev The event.
|
|
72
90
|
*/
|
|
73
91
|
public compositionupdate(ev: Pick<CompositionEvent, 'data'>): void {
|
|
74
|
-
|
|
92
|
+
// Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long
|
|
93
|
+
// compositions
|
|
94
|
+
this._compositionView.textContent = `\u200E${ev.data}\u200E`;
|
|
75
95
|
this.updateCompositionElements();
|
|
76
96
|
setTimeout(() => {
|
|
77
|
-
this.
|
|
97
|
+
const end = this._textarea.selectionEnd ?? this._textarea.value.length;
|
|
98
|
+
this._compositionPosition.end = Math.max( this._compositionPosition.start, end);
|
|
78
99
|
}, 0);
|
|
79
100
|
}
|
|
80
101
|
|
|
@@ -141,6 +162,7 @@ export class CompositionHelper {
|
|
|
141
162
|
start: this._compositionPosition.start,
|
|
142
163
|
end: this._compositionPosition.end
|
|
143
164
|
};
|
|
165
|
+
const currentCompositionSuffix = this._compositionSuffix;
|
|
144
166
|
|
|
145
167
|
// Since composition* events happen before the changes take place in the textarea on most
|
|
146
168
|
// browsers, use a setTimeout with 0ms time to allow the native compositionend event to
|
|
@@ -164,10 +186,14 @@ export class CompositionHelper {
|
|
|
164
186
|
// if a new composition has started.
|
|
165
187
|
input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start);
|
|
166
188
|
} else {
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
|
|
189
|
+
// Keep support for non-composition characters typed immediately after composition end
|
|
190
|
+
// while avoiding re-sending the trailing text that was already present
|
|
191
|
+
// before composition started.
|
|
192
|
+
const value = this._textarea.value;
|
|
193
|
+
const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix)
|
|
194
|
+
? value.length - currentCompositionSuffix.length
|
|
195
|
+
: value.length;
|
|
196
|
+
input = value.substring(currentCompositionPosition.start, Math.max(currentCompositionPosition.start, valueEnd));
|
|
171
197
|
}
|
|
172
198
|
if (input.length > 0) {
|
|
173
199
|
this._coreService.triggerDataEvent(input, true);
|
|
@@ -184,8 +210,12 @@ export class CompositionHelper {
|
|
|
184
210
|
* IME is active.
|
|
185
211
|
*/
|
|
186
212
|
private _handleAnyTextareaChanges(): void {
|
|
213
|
+
if (this._textareaChangeTimer) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
187
216
|
const oldValue = this._textarea.value;
|
|
188
|
-
setTimeout(() => {
|
|
217
|
+
this._textareaChangeTimer = window.setTimeout(() => {
|
|
218
|
+
this._textareaChangeTimer = undefined;
|
|
189
219
|
// Ignore if a composition has started since the timeout
|
|
190
220
|
if (!this._isComposing) {
|
|
191
221
|
const newValue = this._textarea.value;
|
|
@@ -230,6 +260,12 @@ export class CompositionHelper {
|
|
|
230
260
|
this._compositionView.style.lineHeight = cellHeight + 'px';
|
|
231
261
|
this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily;
|
|
232
262
|
this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px';
|
|
263
|
+
// Limit the composition view width to the space between the cursor and
|
|
264
|
+
// the terminal's right edge, preventing it from overflowing the terminal.
|
|
265
|
+
const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft;
|
|
266
|
+
this._compositionView.style.maxWidth = maxWidth + 'px';
|
|
267
|
+
this._compositionView.style.overflow = 'hidden';
|
|
268
|
+
this._compositionView.style.direction = 'rtl';
|
|
233
269
|
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
|
234
270
|
// text is.
|
|
235
271
|
const compositionViewBounds = this._compositionView.getBoundingClientRect();
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
import * as Strings from 'browser/LocalizableStrings';
|
|
7
7
|
import { CoreBrowserTerminal as TerminalCore } from 'browser/CoreBrowserTerminal';
|
|
8
8
|
import { IBufferRange, ITerminal } from 'browser/Types';
|
|
9
|
-
import { Disposable } from '
|
|
9
|
+
import { Disposable } from 'common/Lifecycle';
|
|
10
10
|
import { ITerminalOptions } from 'common/Types';
|
|
11
11
|
import { AddonManager } from 'common/public/AddonManager';
|
|
12
12
|
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
|
13
13
|
import { ParserApi } from 'common/public/ParserApi';
|
|
14
14
|
import { UnicodeApi } from 'common/public/UnicodeApi';
|
|
15
|
-
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
|
16
|
-
import type {
|
|
15
|
+
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, IRenderDimensions, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
|
16
|
+
import type { IEvent } from 'common/Event';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* The set of options that only have an effect when set in the Terminal constructor.
|
|
@@ -68,25 +68,24 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
public get onBell():
|
|
72
|
-
public get onBinary():
|
|
73
|
-
public get onCursorMove():
|
|
74
|
-
public get onData():
|
|
75
|
-
public get onKey():
|
|
76
|
-
public get onLineFeed():
|
|
77
|
-
public get onRender():
|
|
78
|
-
public get onResize():
|
|
79
|
-
public get onScroll():
|
|
80
|
-
public get onSelectionChange():
|
|
81
|
-
public get onTitleChange():
|
|
82
|
-
public get onWriteParsed():
|
|
71
|
+
public get onBell(): IEvent<void> { return this._core.onBell; }
|
|
72
|
+
public get onBinary(): IEvent<string> { return this._core.onBinary; }
|
|
73
|
+
public get onCursorMove(): IEvent<void> { return this._core.onCursorMove; }
|
|
74
|
+
public get onData(): IEvent<string> { return this._core.onData; }
|
|
75
|
+
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; }
|
|
76
|
+
public get onLineFeed(): IEvent<void> { return this._core.onLineFeed; }
|
|
77
|
+
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
|
|
78
|
+
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
|
|
79
|
+
public get onScroll(): IEvent<number> { return this._core.onScroll; }
|
|
80
|
+
public get onSelectionChange(): IEvent<void> { return this._core.onSelectionChange; }
|
|
81
|
+
public get onTitleChange(): IEvent<string> { return this._core.onTitleChange; }
|
|
82
|
+
public get onWriteParsed(): IEvent<void> { return this._core.onWriteParsed; }
|
|
83
|
+
public get onDimensionsChange(): IEvent<IRenderDimensions> { return this._core.onDimensionsChange; }
|
|
83
84
|
|
|
84
85
|
public get element(): HTMLElement | undefined { return this._core.element; }
|
|
86
|
+
public get screenElement(): HTMLElement | undefined { return this._core.screenElement; }
|
|
85
87
|
public get parser(): IParser {
|
|
86
|
-
|
|
87
|
-
this._parser = new ParserApi(this._core);
|
|
88
|
-
}
|
|
89
|
-
return this._parser;
|
|
88
|
+
return this._parser ??= new ParserApi(this._core);
|
|
90
89
|
}
|
|
91
90
|
public get unicode(): IUnicodeHandling {
|
|
92
91
|
this._checkProposedApi();
|
|
@@ -96,19 +95,15 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
96
95
|
public get rows(): number { return this._core.rows; }
|
|
97
96
|
public get cols(): number { return this._core.cols; }
|
|
98
97
|
public get buffer(): IBufferNamespaceApi {
|
|
99
|
-
|
|
100
|
-
this._buffer = this._register(new BufferNamespaceApi(this._core));
|
|
101
|
-
}
|
|
102
|
-
return this._buffer;
|
|
98
|
+
return this._buffer ??= this._register(new BufferNamespaceApi(this._core));
|
|
103
99
|
}
|
|
104
100
|
public get markers(): ReadonlyArray<IMarker> {
|
|
105
|
-
this._checkProposedApi();
|
|
106
101
|
return this._core.markers;
|
|
107
102
|
}
|
|
108
103
|
public get modes(): IModes {
|
|
109
104
|
const m = this._core.coreService.decPrivateModes;
|
|
110
105
|
let mouseTrackingMode: 'none' | 'x10' | 'vt200' | 'drag' | 'any' = 'none';
|
|
111
|
-
switch (this._core.
|
|
106
|
+
switch (this._core.mouseStateService.activeProtocol) {
|
|
112
107
|
case 'X10': mouseTrackingMode = 'x10'; break;
|
|
113
108
|
case 'VT200': mouseTrackingMode = 'vt200'; break;
|
|
114
109
|
case 'DRAG': mouseTrackingMode = 'drag'; break;
|
|
@@ -123,10 +118,15 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
123
118
|
originMode: m.origin,
|
|
124
119
|
reverseWraparoundMode: m.reverseWraparound,
|
|
125
120
|
sendFocusMode: m.sendFocus,
|
|
121
|
+
showCursor: !this._core.coreService.isCursorHidden,
|
|
126
122
|
synchronizedOutputMode: m.synchronizedOutput,
|
|
123
|
+
win32InputMode: m.win32InputMode,
|
|
127
124
|
wraparoundMode: m.wraparound
|
|
128
125
|
};
|
|
129
126
|
}
|
|
127
|
+
public get dimensions(): IRenderDimensions | undefined {
|
|
128
|
+
return this._core.dimensions;
|
|
129
|
+
}
|
|
130
130
|
public get options(): Required<ITerminalOptions> {
|
|
131
131
|
return this._publicOptions;
|
|
132
132
|
}
|
|
@@ -161,11 +161,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
161
161
|
return this._core.registerLinkProvider(linkProvider);
|
|
162
162
|
}
|
|
163
163
|
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
|
|
164
|
-
this._checkProposedApi();
|
|
165
164
|
return this._core.registerCharacterJoiner(handler);
|
|
166
165
|
}
|
|
167
166
|
public deregisterCharacterJoiner(joinerId: number): void {
|
|
168
|
-
this._checkProposedApi();
|
|
169
167
|
this._core.deregisterCharacterJoiner(joinerId);
|
|
170
168
|
}
|
|
171
169
|
public registerMarker(cursorYOffset: number = 0): IMarker {
|
|
@@ -173,7 +171,6 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
173
171
|
return this._core.registerMarker(cursorYOffset);
|
|
174
172
|
}
|
|
175
173
|
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
|
|
176
|
-
this._checkProposedApi();
|
|
177
174
|
this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
|
|
178
175
|
return this._core.registerDecoration(decorationOptions);
|
|
179
176
|
}
|
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory';
|
|
7
7
|
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
|
8
|
-
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
|
8
|
+
import { INVERTED_DEFAULT_COLOR, RendererConstants } from 'browser/renderer/shared/Constants';
|
|
9
9
|
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
|
10
10
|
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
|
11
|
+
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
|
|
11
12
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
12
13
|
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
13
14
|
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
|
14
15
|
import { color } from 'common/Color';
|
|
15
|
-
import { Disposable, toDisposable } from '
|
|
16
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
16
17
|
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
|
17
|
-
import { Emitter } from '
|
|
18
|
+
import { Emitter } from 'common/Event';
|
|
19
|
+
import { addDisposableListener } from 'browser/Dom';
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
|
@@ -23,6 +25,7 @@ const FG_CLASS_PREFIX = 'xterm-fg-';
|
|
|
23
25
|
const BG_CLASS_PREFIX = 'xterm-bg-';
|
|
24
26
|
const FOCUS_CLASS = 'xterm-focus';
|
|
25
27
|
const SELECTION_CLASS = 'xterm-selection';
|
|
28
|
+
const CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle';
|
|
26
29
|
|
|
27
30
|
let nextTerminalId = 1;
|
|
28
31
|
|
|
@@ -42,10 +45,15 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
42
45
|
private _selectionContainer: HTMLElement;
|
|
43
46
|
private _widthCache: WidthCache;
|
|
44
47
|
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
|
48
|
+
private _cursorBlinkStateManager: CursorBlinkStateManager;
|
|
49
|
+
private _textBlinkStateManager: TextBlinkStateManager;
|
|
50
|
+
private _rowHasBlinkingCells: boolean[] = [];
|
|
51
|
+
private _rowHasBlinkingCellsCount: number = 0;
|
|
45
52
|
|
|
46
53
|
public dimensions: IRenderDimensions;
|
|
47
54
|
|
|
48
|
-
|
|
55
|
+
private readonly _onRequestRedraw = this._register(new Emitter<IRequestRedrawEvent>());
|
|
56
|
+
public readonly onRequestRedraw = this._onRequestRedraw.event;
|
|
49
57
|
|
|
50
58
|
constructor(
|
|
51
59
|
private readonly _terminal: ITerminal,
|
|
@@ -89,6 +97,15 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
89
97
|
this._register(this._linkifier2.onShowLinkUnderline(e => this._handleLinkHover(e)));
|
|
90
98
|
this._register(this._linkifier2.onHideLinkUnderline(e => this._handleLinkLeave(e)));
|
|
91
99
|
|
|
100
|
+
this._cursorBlinkStateManager = new CursorBlinkStateManager(this._rowContainer, this._coreBrowserService);
|
|
101
|
+
this._register(addDisposableListener(this._document, 'mousedown', () => this._cursorBlinkStateManager.restartBlinkAnimation()));
|
|
102
|
+
this._register(toDisposable(() => this._cursorBlinkStateManager.dispose()));
|
|
103
|
+
this._textBlinkStateManager = this._register(new TextBlinkStateManager(
|
|
104
|
+
() => this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 }),
|
|
105
|
+
this._coreBrowserService,
|
|
106
|
+
this._optionsService
|
|
107
|
+
));
|
|
108
|
+
|
|
92
109
|
this._register(toDisposable(() => {
|
|
93
110
|
this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
|
94
111
|
|
|
@@ -101,7 +118,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
101
118
|
this._dimensionsStyleElement.remove();
|
|
102
119
|
}));
|
|
103
120
|
|
|
104
|
-
this._widthCache = new WidthCache(
|
|
121
|
+
this._widthCache = new WidthCache();
|
|
105
122
|
this._widthCache.setFont(
|
|
106
123
|
this._optionsService.rawOptions.fontFamily,
|
|
107
124
|
this._optionsService.rawOptions.fontSize,
|
|
@@ -186,6 +203,9 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
186
203
|
`}` +
|
|
187
204
|
`${this._terminalSelector} span.${RowCss.ITALIC_CLASS} {` +
|
|
188
205
|
` font-style: italic;` +
|
|
206
|
+
`}` +
|
|
207
|
+
`${this._terminalSelector} span.${RowCss.BLINK_HIDDEN_CLASS} {` +
|
|
208
|
+
` visibility: hidden;` +
|
|
189
209
|
`}`;
|
|
190
210
|
// Blink animation
|
|
191
211
|
const blinkAnimationUnderlineId = `blink_underline_${this._terminalClass}`;
|
|
@@ -225,6 +245,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
225
245
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
|
226
246
|
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
|
227
247
|
`}` +
|
|
248
|
+
// Disable cursor blinking when idle
|
|
249
|
+
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
|
250
|
+
` animation: none !important;` +
|
|
251
|
+
`}` +
|
|
228
252
|
// !important helps fix an issue where the cursor will not render on top of the selection,
|
|
229
253
|
// however it's very hard to fix this issue and retain the blink animation without the use of
|
|
230
254
|
// !important. So this edge case fails when cursor blink is on.
|
|
@@ -307,10 +331,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
307
331
|
const row = this._document.createElement('div');
|
|
308
332
|
this._rowContainer.appendChild(row);
|
|
309
333
|
this._rowElements.push(row);
|
|
334
|
+
this._rowHasBlinkingCells.push(false);
|
|
310
335
|
}
|
|
311
336
|
// Remove excess elements
|
|
312
337
|
while (this._rowElements.length > rows) {
|
|
313
338
|
this._rowContainer.removeChild(this._rowElements.pop()!);
|
|
339
|
+
if (this._rowHasBlinkingCells.pop()) {
|
|
340
|
+
this._rowHasBlinkingCellsCount--;
|
|
341
|
+
}
|
|
314
342
|
}
|
|
315
343
|
}
|
|
316
344
|
|
|
@@ -328,14 +356,20 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
328
356
|
|
|
329
357
|
public handleBlur(): void {
|
|
330
358
|
this._rowContainer.classList.remove(FOCUS_CLASS);
|
|
359
|
+
this._cursorBlinkStateManager.pause();
|
|
331
360
|
this.renderRows(0, this._bufferService.rows - 1);
|
|
332
361
|
}
|
|
333
362
|
|
|
334
363
|
public handleFocus(): void {
|
|
335
364
|
this._rowContainer.classList.add(FOCUS_CLASS);
|
|
365
|
+
this._cursorBlinkStateManager.resume();
|
|
336
366
|
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
|
337
367
|
}
|
|
338
368
|
|
|
369
|
+
public handleViewportVisibilityChange(isVisible: boolean): void {
|
|
370
|
+
this._textBlinkStateManager.setViewportVisible(isVisible);
|
|
371
|
+
}
|
|
372
|
+
|
|
339
373
|
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
|
340
374
|
// Remove all selections
|
|
341
375
|
this._selectionContainer.replaceChildren();
|
|
@@ -406,7 +440,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
406
440
|
}
|
|
407
441
|
|
|
408
442
|
public handleCursorMove(): void {
|
|
409
|
-
//
|
|
443
|
+
// Reset idle timer on cursor movement (which happens on input)
|
|
444
|
+
this._cursorBlinkStateManager.restartBlinkAnimation();
|
|
410
445
|
}
|
|
411
446
|
|
|
412
447
|
private _handleOptionsChanged(): void {
|
|
@@ -436,6 +471,11 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
436
471
|
*/
|
|
437
472
|
e.replaceChildren();
|
|
438
473
|
}
|
|
474
|
+
if (this._rowHasBlinkingCellsCount > 0) {
|
|
475
|
+
this._rowHasBlinkingCells.fill(false);
|
|
476
|
+
this._rowHasBlinkingCellsCount = 0;
|
|
477
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(false);
|
|
478
|
+
}
|
|
439
479
|
}
|
|
440
480
|
|
|
441
481
|
public renderRows(start: number, end: number): void {
|
|
@@ -445,6 +485,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
445
485
|
const cursorBlink = this._coreService.decPrivateModes.cursorBlink ?? this._optionsService.rawOptions.cursorBlink;
|
|
446
486
|
const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? this._optionsService.rawOptions.cursorStyle;
|
|
447
487
|
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
|
488
|
+
const rowInfo = { hasBlinkingCells: false };
|
|
448
489
|
|
|
449
490
|
for (let y = start; y <= end; y++) {
|
|
450
491
|
const row = y + buffer.ydisp;
|
|
@@ -462,13 +503,17 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
462
503
|
cursorInactiveStyle,
|
|
463
504
|
cursorX,
|
|
464
505
|
cursorBlink,
|
|
506
|
+
this._textBlinkStateManager.isBlinkOn,
|
|
465
507
|
this.dimensions.css.cell.width,
|
|
466
508
|
this._widthCache,
|
|
467
509
|
-1,
|
|
468
|
-
-1
|
|
510
|
+
-1,
|
|
511
|
+
rowInfo
|
|
469
512
|
)
|
|
470
513
|
);
|
|
514
|
+
this._setRowBlinkState(y, rowInfo.hasBlinkingCells);
|
|
471
515
|
}
|
|
516
|
+
this._updateTextBlinkState();
|
|
472
517
|
}
|
|
473
518
|
|
|
474
519
|
private get _terminalSelector(): string {
|
|
@@ -513,6 +558,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
513
558
|
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
|
514
559
|
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
|
515
560
|
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
|
561
|
+
const rowInfo = { hasBlinkingCells: false };
|
|
516
562
|
|
|
517
563
|
// refresh rows within link range
|
|
518
564
|
for (let i = y; i <= y2; ++i) {
|
|
@@ -531,12 +577,86 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
531
577
|
cursorInactiveStyle,
|
|
532
578
|
cursorX,
|
|
533
579
|
cursorBlink,
|
|
580
|
+
this._textBlinkStateManager.isBlinkOn,
|
|
534
581
|
this.dimensions.css.cell.width,
|
|
535
582
|
this._widthCache,
|
|
536
583
|
enabled ? (i === y ? x : 0) : -1,
|
|
537
|
-
enabled ? ((i === y2 ? x2 : cols) - 1) : -1
|
|
584
|
+
enabled ? ((i === y2 ? x2 : cols) - 1) : -1,
|
|
585
|
+
rowInfo
|
|
538
586
|
)
|
|
539
587
|
);
|
|
588
|
+
this._setRowBlinkState(i, rowInfo.hasBlinkingCells);
|
|
540
589
|
}
|
|
590
|
+
this._updateTextBlinkState();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void {
|
|
594
|
+
const previous = this._rowHasBlinkingCells[row];
|
|
595
|
+
if (previous === hasBlinkingCells) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
this._rowHasBlinkingCells[row] = hasBlinkingCells;
|
|
599
|
+
this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private _updateTextBlinkState(): void {
|
|
603
|
+
this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
class CursorBlinkStateManager {
|
|
608
|
+
private _idleTimeout: number | undefined;
|
|
609
|
+
private _isIdlePaused: boolean = false;
|
|
610
|
+
|
|
611
|
+
constructor(
|
|
612
|
+
private readonly _rowContainer: HTMLElement,
|
|
613
|
+
private readonly _coreBrowserService: ICoreBrowserService
|
|
614
|
+
) {
|
|
615
|
+
if (this._coreBrowserService.isFocused) {
|
|
616
|
+
this._resetIdleTimer();
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
public dispose(): void {
|
|
621
|
+
this._clearIdleTimer();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
public restartBlinkAnimation(): void {
|
|
625
|
+
if (this._isIdlePaused) {
|
|
626
|
+
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
|
627
|
+
}
|
|
628
|
+
this._resetIdleTimer();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
public pause(): void {
|
|
632
|
+
this._isIdlePaused = false;
|
|
633
|
+
this._clearIdleTimer();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
public resume(): void {
|
|
637
|
+
this._isIdlePaused = false;
|
|
638
|
+
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
|
639
|
+
this._resetIdleTimer();
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
private _resetIdleTimer(): void {
|
|
643
|
+
this._isIdlePaused = false;
|
|
644
|
+
this._clearIdleTimer();
|
|
645
|
+
this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
|
|
646
|
+
this._stopBlinkingDueToIdle();
|
|
647
|
+
}, RendererConstants.CURSOR_BLINK_IDLE_TIMEOUT);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
private _clearIdleTimer(): void {
|
|
651
|
+
if (this._idleTimeout) {
|
|
652
|
+
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
|
653
|
+
this._idleTimeout = undefined;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private _stopBlinkingDueToIdle(): void {
|
|
658
|
+
this._rowContainer.classList.add(CURSOR_BLINK_IDLE_CLASS);
|
|
659
|
+
this._isIdlePaused = true;
|
|
660
|
+
this._idleTimeout = undefined;
|
|
541
661
|
}
|
|
542
662
|
}
|
|
@@ -24,6 +24,7 @@ export const enum RowCss {
|
|
|
24
24
|
UNDERLINE_CLASS = 'xterm-underline',
|
|
25
25
|
OVERLINE_CLASS = 'xterm-overline',
|
|
26
26
|
STRIKETHROUGH_CLASS = 'xterm-strikethrough',
|
|
27
|
+
BLINK_HIDDEN_CLASS = 'xterm-blink-hidden',
|
|
27
28
|
CURSOR_CLASS = 'xterm-cursor',
|
|
28
29
|
CURSOR_BLINK_CLASS = 'xterm-cursor-blink',
|
|
29
30
|
CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block',
|
|
@@ -66,13 +67,18 @@ export class DomRendererRowFactory {
|
|
|
66
67
|
cursorInactiveStyle: string | undefined,
|
|
67
68
|
cursorX: number,
|
|
68
69
|
cursorBlink: boolean,
|
|
70
|
+
blinkOn: boolean,
|
|
69
71
|
cellWidth: number,
|
|
70
72
|
widthCache: WidthCache,
|
|
71
73
|
linkStart: number,
|
|
72
|
-
linkEnd: number
|
|
74
|
+
linkEnd: number,
|
|
75
|
+
rowInfo?: { hasBlinkingCells: boolean }
|
|
73
76
|
): HTMLSpanElement[] {
|
|
74
77
|
|
|
75
78
|
const elements: HTMLSpanElement[] = [];
|
|
79
|
+
if (rowInfo) {
|
|
80
|
+
rowInfo.hasBlinkingCells = false;
|
|
81
|
+
}
|
|
76
82
|
const joinedRanges = this._characterJoinerService.getJoinedCharacters(row);
|
|
77
83
|
const colors = this._themeService.colors;
|
|
78
84
|
|
|
@@ -118,7 +124,7 @@ export class DomRendererRowFactory {
|
|
|
118
124
|
// Process any joined character ranges as needed. Because of how the
|
|
119
125
|
// ranges are produced, we know that they are valid for the characters
|
|
120
126
|
// and attributes of our input.
|
|
121
|
-
let cell = this._workCell;
|
|
127
|
+
let cell: ICellData = this._workCell;
|
|
122
128
|
if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) {
|
|
123
129
|
const range = joinedRanges.shift()!;
|
|
124
130
|
// If the ligature's selection state is not consistent, don't join it. This helps the
|
|
@@ -153,6 +159,13 @@ export class DomRendererRowFactory {
|
|
|
153
159
|
const isInSelection = this._isCellInSelection(x, row);
|
|
154
160
|
const isCursorCell = isCursorRow && x === cursorX;
|
|
155
161
|
const isLinkHover = hasHover && x >= linkStart && x <= linkEnd;
|
|
162
|
+
if (rowInfo && cell.isBlink()) {
|
|
163
|
+
rowInfo.hasBlinkingCells = true;
|
|
164
|
+
}
|
|
165
|
+
const isBlinkHidden = !blinkOn && cell.isBlink();
|
|
166
|
+
if (isBlinkHidden) {
|
|
167
|
+
classes.push(RowCss.BLINK_HIDDEN_CLASS);
|
|
168
|
+
}
|
|
156
169
|
|
|
157
170
|
let isDecorated = false;
|
|
158
171
|
this._decorationService.forEachDecorationAtCell(x, row, undefined, d => {
|
|
@@ -397,7 +410,7 @@ export class DomRendererRowFactory {
|
|
|
397
410
|
break;
|
|
398
411
|
case Attributes.CM_RGB:
|
|
399
412
|
resolvedBg = channels.toColor(bg >> 16, bg >> 8 & 0xFF, bg & 0xFF);
|
|
400
|
-
this._addStyle(charElement, `background-color:#${
|
|
413
|
+
this._addStyle(charElement, `background-color:#${(bg >>> 0).toString(16).padStart(6, '0')}`);
|
|
401
414
|
break;
|
|
402
415
|
case Attributes.CM_DEFAULT:
|
|
403
416
|
default:
|
|
@@ -434,7 +447,7 @@ export class DomRendererRowFactory {
|
|
|
434
447
|
(fg ) & 0xFF
|
|
435
448
|
);
|
|
436
449
|
if (!this._applyMinimumContrast(charElement, resolvedBg, color, cell, bgOverride, fgOverride)) {
|
|
437
|
-
this._addStyle(charElement, `color:#${
|
|
450
|
+
this._addStyle(charElement, `color:#${fg.toString(16).padStart(6, '0')}`);
|
|
438
451
|
}
|
|
439
452
|
break;
|
|
440
453
|
case Attributes.CM_DEFAULT:
|
|
@@ -494,8 +507,8 @@ export class DomRendererRowFactory {
|
|
|
494
507
|
// Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from
|
|
495
508
|
// non-dim cells
|
|
496
509
|
const ratio = this._optionsService.rawOptions.minimumContrastRatio / (cell.isDim() ? 2 : 1);
|
|
497
|
-
adjustedColor = color.ensureContrastRatio(bgOverride
|
|
498
|
-
cache.setColor((bgOverride
|
|
510
|
+
adjustedColor = color.ensureContrastRatio(bgOverride ?? bg, fgOverride ?? fg, ratio);
|
|
511
|
+
cache.setColor((bgOverride ?? bg).rgba, (fgOverride ?? fg).rgba, adjustedColor ?? null);
|
|
499
512
|
}
|
|
500
513
|
|
|
501
514
|
if (adjustedColor) {
|
|
@@ -537,10 +550,3 @@ export class DomRendererRowFactory {
|
|
|
537
550
|
(start[1] < end[1] && y === start[1] && x >= start[0]);
|
|
538
551
|
}
|
|
539
552
|
}
|
|
540
|
-
|
|
541
|
-
function padStart(text: string, padChar: string, length: number): string {
|
|
542
|
-
while (text.length < length) {
|
|
543
|
-
text = padChar + text;
|
|
544
|
-
}
|
|
545
|
-
return text;
|
|
546
|
-
}
|