@xterm/xterm 5.4.0-beta.3 → 5.4.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/css/xterm.css +10 -1
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/package.json +3 -3
- package/src/browser/AccessibilityManager.ts +132 -3
- package/src/browser/{Linkifier2.ts → Linkifier.ts} +36 -61
- package/src/browser/OscLinkProvider.ts +2 -1
- package/src/browser/RenderDebouncer.ts +6 -5
- package/src/browser/Terminal.ts +41 -33
- package/src/browser/Types.d.ts +2 -9
- package/src/browser/public/Terminal.ts +3 -0
- package/src/browser/renderer/dom/DomRenderer.ts +20 -9
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +5 -5
- package/src/browser/renderer/shared/CellColorResolver.ts +110 -11
- package/src/browser/renderer/shared/RendererUtils.ts +5 -1
- package/src/browser/renderer/shared/SelectionRenderModel.ts +5 -3
- package/src/browser/renderer/shared/TextureAtlas.ts +31 -13
- package/src/browser/renderer/shared/Types.d.ts +2 -2
- package/src/browser/services/CharSizeService.ts +53 -28
- package/src/browser/services/LinkProviderService.ts +28 -0
- package/src/browser/services/RenderService.ts +13 -8
- package/src/browser/services/Services.ts +12 -1
- package/src/common/Color.ts +28 -12
- package/src/common/CoreTerminal.ts +1 -0
- package/src/common/InputHandler.ts +15 -19
- package/src/common/Types.d.ts +9 -7
- package/src/common/buffer/AttributeData.ts +15 -0
- package/src/common/buffer/BufferLine.ts +47 -20
- package/src/common/buffer/Constants.ts +10 -2
- package/src/common/input/Keyboard.ts +2 -3
- package/src/common/services/OptionsService.ts +8 -1
- package/typings/xterm.d.ts +22 -0
package/src/browser/Terminal.ts
CHANGED
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
|
|
24
24
|
import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from 'browser/Clipboard';
|
|
25
25
|
import { addDisposableDomListener } from 'browser/Lifecycle';
|
|
26
|
-
import {
|
|
26
|
+
import { Linkifier } from './Linkifier';
|
|
27
27
|
import * as Strings from 'browser/LocalizableStrings';
|
|
28
28
|
import { OscLinkProvider } from 'browser/OscLinkProvider';
|
|
29
|
-
import { CharacterJoinerHandler, CustomKeyEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal, IViewport } from 'browser/Types';
|
|
29
|
+
import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal, IViewport } from 'browser/Types';
|
|
30
30
|
import { Viewport } from 'browser/Viewport';
|
|
31
31
|
import { BufferDecorationRenderer } from 'browser/decorations/BufferDecorationRenderer';
|
|
32
32
|
import { OverviewRulerRenderer } from 'browser/decorations/OverviewRulerRenderer';
|
|
@@ -39,9 +39,9 @@ import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
|
|
39
39
|
import { MouseService } from 'browser/services/MouseService';
|
|
40
40
|
import { RenderService } from 'browser/services/RenderService';
|
|
41
41
|
import { SelectionService } from 'browser/services/SelectionService';
|
|
42
|
-
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
|
42
|
+
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, ILinkProviderService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
|
43
43
|
import { ThemeService } from 'browser/services/ThemeService';
|
|
44
|
-
import {
|
|
44
|
+
import { channels, color } from 'common/Color';
|
|
45
45
|
import { CoreTerminal } from 'common/CoreTerminal';
|
|
46
46
|
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
|
|
47
47
|
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
|
|
@@ -57,6 +57,7 @@ import { IDecorationService } from 'common/services/Services';
|
|
|
57
57
|
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
|
|
58
58
|
import { WindowsOptionsReportType } from '../common/InputHandler';
|
|
59
59
|
import { AccessibilityManager } from './AccessibilityManager';
|
|
60
|
+
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
|
60
61
|
|
|
61
62
|
export class Terminal extends CoreTerminal implements ITerminal {
|
|
62
63
|
public textarea: HTMLTextAreaElement | undefined;
|
|
@@ -69,14 +70,19 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
69
70
|
private _helperContainer: HTMLElement | undefined;
|
|
70
71
|
private _compositionView: HTMLElement | undefined;
|
|
71
72
|
|
|
73
|
+
public linkifier: ILinkifier2 | undefined;
|
|
72
74
|
private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
|
|
73
75
|
|
|
74
76
|
public browser: IBrowser = Browser as any;
|
|
75
77
|
|
|
76
78
|
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
|
|
79
|
+
private _customWheelEventHandler: CustomWheelEventHandler | undefined;
|
|
77
80
|
|
|
78
|
-
//
|
|
81
|
+
// Browser services
|
|
79
82
|
private _decorationService: DecorationService;
|
|
83
|
+
private _linkProviderService: ILinkProviderService;
|
|
84
|
+
|
|
85
|
+
// Optional browser services
|
|
80
86
|
private _charSizeService: ICharSizeService | undefined;
|
|
81
87
|
private _coreBrowserService: ICoreBrowserService | undefined;
|
|
82
88
|
private _mouseService: IMouseService | undefined;
|
|
@@ -112,7 +118,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
112
118
|
*/
|
|
113
119
|
private _unprocessedDeadKey: boolean = false;
|
|
114
120
|
|
|
115
|
-
public linkifier2: ILinkifier2;
|
|
116
121
|
public viewport: IViewport | undefined;
|
|
117
122
|
private _compositionHelper: ICompositionHelper | undefined;
|
|
118
123
|
private _accessibilityManager: MutableDisposable<AccessibilityManager> = this.register(new MutableDisposable());
|
|
@@ -148,10 +153,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
148
153
|
|
|
149
154
|
this._setup();
|
|
150
155
|
|
|
151
|
-
this.linkifier2 = this.register(this._instantiationService.createInstance(Linkifier2));
|
|
152
|
-
this.linkifier2.registerLinkProvider(this._instantiationService.createInstance(OscLinkProvider));
|
|
153
156
|
this._decorationService = this._instantiationService.createInstance(DecorationService);
|
|
154
157
|
this._instantiationService.setService(IDecorationService, this._decorationService);
|
|
158
|
+
this._linkProviderService = this._instantiationService.createInstance(LinkProviderService);
|
|
159
|
+
this._instantiationService.setService(ILinkProviderService, this._linkProviderService);
|
|
160
|
+
this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(OscLinkProvider));
|
|
155
161
|
|
|
156
162
|
// Setup InputHandler listeners
|
|
157
163
|
this.register(this._inputHandler.onRequestBell(() => this._onBell.fire()));
|
|
@@ -205,17 +211,17 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
205
211
|
}
|
|
206
212
|
switch (req.type) {
|
|
207
213
|
case ColorRequestType.REPORT:
|
|
208
|
-
const
|
|
214
|
+
const colorRgb = color.toColorRGB(acc === 'ansi'
|
|
209
215
|
? this._themeService.colors.ansi[req.index]
|
|
210
216
|
: this._themeService.colors[acc]);
|
|
211
|
-
this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(
|
|
217
|
+
this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(colorRgb)}${C1_ESCAPED.ST}`);
|
|
212
218
|
break;
|
|
213
219
|
case ColorRequestType.SET:
|
|
214
220
|
if (acc === 'ansi') {
|
|
215
|
-
this._themeService.modifyColors(colors => colors.ansi[req.index] =
|
|
221
|
+
this._themeService.modifyColors(colors => colors.ansi[req.index] = channels.toColor(...req.color));
|
|
216
222
|
} else {
|
|
217
223
|
const narrowedAcc = acc;
|
|
218
|
-
this._themeService.modifyColors(colors => colors[narrowedAcc] =
|
|
224
|
+
this._themeService.modifyColors(colors => colors[narrowedAcc] = channels.toColor(...req.color));
|
|
219
225
|
}
|
|
220
226
|
break;
|
|
221
227
|
case ColorRequestType.RESTORE:
|
|
@@ -260,11 +266,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
260
266
|
/**
|
|
261
267
|
* Binds the desired focus behavior on a given terminal object.
|
|
262
268
|
*/
|
|
263
|
-
private _handleTextAreaFocus(ev:
|
|
269
|
+
private _handleTextAreaFocus(ev: FocusEvent): void {
|
|
264
270
|
if (this.coreService.decPrivateModes.sendFocus) {
|
|
265
271
|
this.coreService.triggerDataEvent(C0.ESC + '[I');
|
|
266
272
|
}
|
|
267
|
-
this.updateCursorStyle(ev);
|
|
268
273
|
this.element!.classList.add('focus');
|
|
269
274
|
this._showCursor();
|
|
270
275
|
this._onFocus.fire();
|
|
@@ -428,6 +433,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
428
433
|
|
|
429
434
|
this.screenElement = this._document.createElement('div');
|
|
430
435
|
this.screenElement.classList.add('xterm-screen');
|
|
436
|
+
this.register(addDisposableDomListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev)));
|
|
431
437
|
// Create the container that will hold helpers like the textarea for
|
|
432
438
|
// capturing DOM Events. Then produce the helpers.
|
|
433
439
|
this._helperContainer = this._document.createElement('div');
|
|
@@ -458,11 +464,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
458
464
|
));
|
|
459
465
|
this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService);
|
|
460
466
|
|
|
461
|
-
this.register(addDisposableDomListener(this.textarea, 'focus', (ev:
|
|
467
|
+
this.register(addDisposableDomListener(this.textarea, 'focus', (ev: FocusEvent) => this._handleTextAreaFocus(ev)));
|
|
462
468
|
this.register(addDisposableDomListener(this.textarea, 'blur', () => this._handleTextAreaBlur()));
|
|
463
469
|
this._helperContainer.appendChild(this.textarea);
|
|
464
470
|
|
|
465
|
-
|
|
466
471
|
this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
|
|
467
472
|
this._instantiationService.setService(ICharSizeService, this._charSizeService);
|
|
468
473
|
|
|
@@ -482,6 +487,11 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
482
487
|
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
|
|
483
488
|
this._helperContainer.appendChild(this._compositionView);
|
|
484
489
|
|
|
490
|
+
this._mouseService = this._instantiationService.createInstance(MouseService);
|
|
491
|
+
this._instantiationService.setService(IMouseService, this._mouseService);
|
|
492
|
+
|
|
493
|
+
this.linkifier = this.register(this._instantiationService.createInstance(Linkifier, this.screenElement));
|
|
494
|
+
|
|
485
495
|
// Performance: Add viewport and helper elements from the fragment
|
|
486
496
|
this.element.appendChild(fragment);
|
|
487
497
|
|
|
@@ -493,9 +503,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
493
503
|
this._renderService.setRenderer(this._createRenderer());
|
|
494
504
|
}
|
|
495
505
|
|
|
496
|
-
this._mouseService = this._instantiationService.createInstance(MouseService);
|
|
497
|
-
this._instantiationService.setService(IMouseService, this._mouseService);
|
|
498
|
-
|
|
499
506
|
this.viewport = this._instantiationService.createInstance(Viewport, this._viewportElement, this._viewportScrollArea);
|
|
500
507
|
this.viewport.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent, ScrollSource.VIEWPORT)),
|
|
501
508
|
this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
|
|
@@ -513,7 +520,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
513
520
|
this._selectionService = this.register(this._instantiationService.createInstance(SelectionService,
|
|
514
521
|
this.element,
|
|
515
522
|
this.screenElement,
|
|
516
|
-
this.
|
|
523
|
+
this.linkifier
|
|
517
524
|
));
|
|
518
525
|
this._instantiationService.setService(ISelectionService, this._selectionService);
|
|
519
526
|
this.register(this._selectionService.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent)));
|
|
@@ -533,7 +540,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
533
540
|
}));
|
|
534
541
|
this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService!.refresh()));
|
|
535
542
|
|
|
536
|
-
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
|
|
537
543
|
this.register(this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement));
|
|
538
544
|
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e)));
|
|
539
545
|
|
|
@@ -575,7 +581,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
575
581
|
}
|
|
576
582
|
|
|
577
583
|
private _createRenderer(): IRenderer {
|
|
578
|
-
return this._instantiationService.createInstance(DomRenderer, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.
|
|
584
|
+
return this._instantiationService.createInstance(DomRenderer, this, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier!);
|
|
579
585
|
}
|
|
580
586
|
|
|
581
587
|
/**
|
|
@@ -633,6 +639,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
633
639
|
but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
|
|
634
640
|
break;
|
|
635
641
|
case 'wheel':
|
|
642
|
+
if (self._customWheelEventHandler && self._customWheelEventHandler(ev as WheelEvent) === false) {
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
636
645
|
const amount = self.viewport!.getLinesScrolled(ev as WheelEvent);
|
|
637
646
|
|
|
638
647
|
if (amount === 0) {
|
|
@@ -792,6 +801,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
792
801
|
// do nothing, if app side handles wheel itself
|
|
793
802
|
if (requestedEvents.wheel) return;
|
|
794
803
|
|
|
804
|
+
if (this._customWheelEventHandler && this._customWheelEventHandler(ev) === false) {
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
|
|
795
808
|
if (!this.buffer.hasScrollback) {
|
|
796
809
|
// Convert wheel events into up/down events when the buffer does not have scrollback, this
|
|
797
810
|
// enables scrolling in apps hosted in the alt buffer such as vim or tmux.
|
|
@@ -847,7 +860,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
847
860
|
/**
|
|
848
861
|
* Change the cursor style for different selection modes
|
|
849
862
|
*/
|
|
850
|
-
public updateCursorStyle(ev: KeyboardEvent): void {
|
|
863
|
+
public updateCursorStyle(ev: KeyboardEvent | MouseEvent): void {
|
|
851
864
|
if (this._selectionService?.shouldColumnSelect(ev)) {
|
|
852
865
|
this.element!.classList.add('column-select');
|
|
853
866
|
} else {
|
|
@@ -878,21 +891,16 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
878
891
|
paste(data, this.textarea!, this.coreService, this.optionsService);
|
|
879
892
|
}
|
|
880
893
|
|
|
881
|
-
/**
|
|
882
|
-
* Attaches a custom key event handler which is run before keys are processed,
|
|
883
|
-
* giving consumers of xterm.js ultimate control as to what keys should be
|
|
884
|
-
* processed by the terminal and what keys should not.
|
|
885
|
-
* @param customKeyEventHandler The custom KeyboardEvent handler to attach.
|
|
886
|
-
* This is a function that takes a KeyboardEvent, allowing consumers to stop
|
|
887
|
-
* propagation and/or prevent the default action. The function returns whether
|
|
888
|
-
* the event should be processed by xterm.js.
|
|
889
|
-
*/
|
|
890
894
|
public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void {
|
|
891
895
|
this._customKeyEventHandler = customKeyEventHandler;
|
|
892
896
|
}
|
|
893
897
|
|
|
898
|
+
public attachCustomWheelEventHandler(customWheelEventHandler: CustomWheelEventHandler): void {
|
|
899
|
+
this._customWheelEventHandler = customWheelEventHandler;
|
|
900
|
+
}
|
|
901
|
+
|
|
894
902
|
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
|
895
|
-
return this.
|
|
903
|
+
return this._linkProviderService.registerLinkProvider(linkProvider);
|
|
896
904
|
}
|
|
897
905
|
|
|
898
906
|
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
|
package/src/browser/Types.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { IEvent } from 'common/EventEmitter';
|
|
|
7
7
|
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
|
|
8
8
|
import { IBuffer } from 'common/buffer/Types';
|
|
9
9
|
import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm';
|
|
10
|
-
import { IMouseService, IRenderService } from './services/Services';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* A portion of the public API that are implemented identially internally and simply passed through.
|
|
@@ -18,9 +17,9 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal {
|
|
|
18
17
|
screenElement: HTMLElement | undefined;
|
|
19
18
|
browser: IBrowser;
|
|
20
19
|
buffer: IBuffer;
|
|
20
|
+
linkifier: ILinkifier2 | undefined;
|
|
21
21
|
viewport: IViewport | undefined;
|
|
22
22
|
options: Required<ITerminalOptions>;
|
|
23
|
-
linkifier2: ILinkifier2;
|
|
24
23
|
|
|
25
24
|
onBlur: IEvent<void>;
|
|
26
25
|
onFocus: IEvent<void>;
|
|
@@ -32,6 +31,7 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
|
|
34
|
+
export type CustomWheelEventHandler = (event: WheelEvent) => boolean;
|
|
35
35
|
|
|
36
36
|
export type LineData = CharData[];
|
|
37
37
|
|
|
@@ -127,13 +127,6 @@ export interface ILinkifier2 extends IDisposable {
|
|
|
127
127
|
onShowLinkUnderline: IEvent<ILinkifierEvent>;
|
|
128
128
|
onHideLinkUnderline: IEvent<ILinkifierEvent>;
|
|
129
129
|
readonly currentLink: ILinkWithState | undefined;
|
|
130
|
-
|
|
131
|
-
attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void;
|
|
132
|
-
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
interface ILinkProvider {
|
|
136
|
-
provideLinks(y: number, callback: (links: ILink[] | undefined) => void): void;
|
|
137
130
|
}
|
|
138
131
|
|
|
139
132
|
interface ILink {
|
|
@@ -148,6 +148,9 @@ export class Terminal extends Disposable implements ITerminalApi {
|
|
|
148
148
|
public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
|
|
149
149
|
this._core.attachCustomKeyEventHandler(customKeyEventHandler);
|
|
150
150
|
}
|
|
151
|
+
public attachCustomWheelEventHandler(customWheelEventHandler: (event: WheelEvent) => boolean): void {
|
|
152
|
+
this._core.attachCustomWheelEventHandler(customWheelEventHandler);
|
|
153
|
+
}
|
|
151
154
|
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
|
|
152
155
|
return this._core.registerLinkProvider(linkProvider);
|
|
153
156
|
}
|
|
@@ -7,9 +7,10 @@ import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererR
|
|
|
7
7
|
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
|
8
8
|
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
|
9
9
|
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
|
10
|
-
import {
|
|
10
|
+
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
|
11
|
+
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
11
12
|
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
12
|
-
import { ILinkifier2, ILinkifierEvent, ReadonlyColorSet } from 'browser/Types';
|
|
13
|
+
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
|
13
14
|
import { color } from 'common/Color';
|
|
14
15
|
import { EventEmitter } from 'common/EventEmitter';
|
|
15
16
|
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
@@ -25,7 +26,6 @@ const SELECTION_CLASS = 'xterm-selection';
|
|
|
25
26
|
|
|
26
27
|
let nextTerminalId = 1;
|
|
27
28
|
|
|
28
|
-
|
|
29
29
|
/**
|
|
30
30
|
* A fallback renderer for when canvas is slow. This is not meant to be
|
|
31
31
|
* particularly fast or feature complete, more just stable and usable for when
|
|
@@ -41,12 +41,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
41
41
|
private _rowElements: HTMLElement[] = [];
|
|
42
42
|
private _selectionContainer: HTMLElement;
|
|
43
43
|
private _widthCache: WidthCache;
|
|
44
|
+
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
|
44
45
|
|
|
45
46
|
public dimensions: IRenderDimensions;
|
|
46
47
|
|
|
47
48
|
public readonly onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>()).event;
|
|
48
49
|
|
|
49
50
|
constructor(
|
|
51
|
+
private readonly _terminal: ITerminal,
|
|
50
52
|
private readonly _document: Document,
|
|
51
53
|
private readonly _element: HTMLElement,
|
|
52
54
|
private readonly _screenElement: HTMLElement,
|
|
@@ -291,6 +293,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
291
293
|
public handleResize(cols: number, rows: number): void {
|
|
292
294
|
this._refreshRowElements(cols, rows);
|
|
293
295
|
this._updateDimensions();
|
|
296
|
+
this.handleSelectionChanged(this._selectionRenderModel.selectionStart, this._selectionRenderModel.selectionEnd, this._selectionRenderModel.columnSelectMode);
|
|
294
297
|
}
|
|
295
298
|
|
|
296
299
|
public handleCharSizeChanged(): void {
|
|
@@ -320,11 +323,13 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
320
323
|
return;
|
|
321
324
|
}
|
|
322
325
|
|
|
326
|
+
this._selectionRenderModel.update(this._terminal, start, end, columnSelectMode);
|
|
327
|
+
|
|
323
328
|
// Translate from buffer position to viewport position
|
|
324
|
-
const viewportStartRow =
|
|
325
|
-
const viewportEndRow =
|
|
326
|
-
const viewportCappedStartRow =
|
|
327
|
-
const viewportCappedEndRow =
|
|
329
|
+
const viewportStartRow = this._selectionRenderModel.viewportStartRow;
|
|
330
|
+
const viewportEndRow = this._selectionRenderModel.viewportEndRow;
|
|
331
|
+
const viewportCappedStartRow = this._selectionRenderModel.viewportCappedStartRow;
|
|
332
|
+
const viewportCappedEndRow = this._selectionRenderModel.viewportCappedEndRow;
|
|
328
333
|
|
|
329
334
|
// No need to draw the selection
|
|
330
335
|
if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
|
|
@@ -365,10 +370,16 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|
|
365
370
|
*/
|
|
366
371
|
private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement {
|
|
367
372
|
const element = this._document.createElement('div');
|
|
373
|
+
const left = colStart * this.dimensions.css.cell.width;
|
|
374
|
+
let width = this.dimensions.css.cell.width * (colEnd - colStart);
|
|
375
|
+
if (left + width > this.dimensions.css.canvas.width) {
|
|
376
|
+
width = this.dimensions.css.canvas.width - left;
|
|
377
|
+
}
|
|
378
|
+
|
|
368
379
|
element.style.height = `${rowCount * this.dimensions.css.cell.height}px`;
|
|
369
380
|
element.style.top = `${row * this.dimensions.css.cell.height}px`;
|
|
370
|
-
element.style.left = `${
|
|
371
|
-
element.style.width = `${
|
|
381
|
+
element.style.left = `${left}px`;
|
|
382
|
+
element.style.width = `${width}px`;
|
|
372
383
|
return element;
|
|
373
384
|
}
|
|
374
385
|
|
|
@@ -8,10 +8,10 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
|
|
8
8
|
import { WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants';
|
|
9
9
|
import { CellData } from 'common/buffer/CellData';
|
|
10
10
|
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
|
11
|
-
import {
|
|
11
|
+
import { channels, color } from 'common/Color';
|
|
12
12
|
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
13
13
|
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
|
14
|
-
import {
|
|
14
|
+
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
|
|
15
15
|
import { AttributeData } from 'common/buffer/AttributeData';
|
|
16
16
|
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
|
17
17
|
import { IColorContrastCache } from 'browser/Types';
|
|
@@ -376,7 +376,7 @@ export class DomRendererRowFactory {
|
|
|
376
376
|
classes.push(`xterm-bg-${bg}`);
|
|
377
377
|
break;
|
|
378
378
|
case Attributes.CM_RGB:
|
|
379
|
-
resolvedBg =
|
|
379
|
+
resolvedBg = channels.toColor(bg >> 16, bg >> 8 & 0xFF, bg & 0xFF);
|
|
380
380
|
this._addStyle(charElement, `background-color:#${padStart((bg >>> 0).toString(16), '0', 6)}`);
|
|
381
381
|
break;
|
|
382
382
|
case Attributes.CM_DEFAULT:
|
|
@@ -408,7 +408,7 @@ export class DomRendererRowFactory {
|
|
|
408
408
|
}
|
|
409
409
|
break;
|
|
410
410
|
case Attributes.CM_RGB:
|
|
411
|
-
const color =
|
|
411
|
+
const color = channels.toColor(
|
|
412
412
|
(fg >> 16) & 0xFF,
|
|
413
413
|
(fg >> 8) & 0xFF,
|
|
414
414
|
(fg ) & 0xFF
|
|
@@ -458,7 +458,7 @@ export class DomRendererRowFactory {
|
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
private _applyMinimumContrast(element: HTMLElement, bg: IColor, fg: IColor, cell: ICellData, bgOverride: IColor | undefined, fgOverride: IColor | undefined): boolean {
|
|
461
|
-
if (this._optionsService.rawOptions.minimumContrastRatio === 1 ||
|
|
461
|
+
if (this._optionsService.rawOptions.minimumContrastRatio === 1 || treatGlyphAsBackgroundColor(cell.getCode())) {
|
|
462
462
|
return false;
|
|
463
463
|
}
|
|
464
464
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
2
2
|
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
3
3
|
import { ReadonlyColorSet } from 'browser/Types';
|
|
4
|
-
import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants';
|
|
5
|
-
import { IDecorationService } from 'common/services/Services';
|
|
4
|
+
import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants';
|
|
5
|
+
import { IDecorationService, IOptionsService } from 'common/services/Services';
|
|
6
6
|
import { ICellData } from 'common/Types';
|
|
7
7
|
import { Terminal } from '@xterm/xterm';
|
|
8
|
+
import { rgba } from 'common/Color';
|
|
9
|
+
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
|
|
8
10
|
|
|
9
11
|
// Work variables to avoid garbage collection
|
|
10
12
|
let $fg = 0;
|
|
@@ -13,6 +15,7 @@ let $hasFg = false;
|
|
|
13
15
|
let $hasBg = false;
|
|
14
16
|
let $isSelected = false;
|
|
15
17
|
let $colors: ReadonlyColorSet | undefined;
|
|
18
|
+
let $variantOffset = 0;
|
|
16
19
|
|
|
17
20
|
export class CellColorResolver {
|
|
18
21
|
/**
|
|
@@ -27,6 +30,7 @@ export class CellColorResolver {
|
|
|
27
30
|
|
|
28
31
|
constructor(
|
|
29
32
|
private readonly _terminal: Terminal,
|
|
33
|
+
private readonly _optionService: IOptionsService,
|
|
30
34
|
private readonly _selectionRenderModel: ISelectionRenderModel,
|
|
31
35
|
private readonly _decorationService: IDecorationService,
|
|
32
36
|
private readonly _coreBrowserService: ICoreBrowserService,
|
|
@@ -38,7 +42,7 @@ export class CellColorResolver {
|
|
|
38
42
|
* Resolves colors for the cell, putting the result into the shared {@link result}. This resolves
|
|
39
43
|
* overrides, inverse and selection for the cell which can then be used to feed into the renderer.
|
|
40
44
|
*/
|
|
41
|
-
public resolve(cell: ICellData, x: number, y: number): void {
|
|
45
|
+
public resolve(cell: ICellData, x: number, y: number, deviceCellWidth: number): void {
|
|
42
46
|
this.result.bg = cell.bg;
|
|
43
47
|
this.result.fg = cell.fg;
|
|
44
48
|
this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
|
|
@@ -52,15 +56,22 @@ export class CellColorResolver {
|
|
|
52
56
|
$hasFg = false;
|
|
53
57
|
$isSelected = false;
|
|
54
58
|
$colors = this._themeService.colors;
|
|
59
|
+
$variantOffset = 0;
|
|
60
|
+
|
|
61
|
+
const code = cell.getCode();
|
|
62
|
+
if (code !== NULL_CELL_CODE && cell.extended.underlineStyle === UnderlineStyle.DOTTED) {
|
|
63
|
+
const lineWidth = Math.max(1, Math.floor(this._optionService.rawOptions.fontSize * this._coreBrowserService.dpr / 15));
|
|
64
|
+
$variantOffset = x * deviceCellWidth % (Math.round(lineWidth) * 2);
|
|
65
|
+
}
|
|
55
66
|
|
|
56
67
|
// Apply decorations on the bottom layer
|
|
57
68
|
this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
|
|
58
69
|
if (d.backgroundColorRGB) {
|
|
59
|
-
$bg = d.backgroundColorRGB.rgba >> 8 &
|
|
70
|
+
$bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
|
|
60
71
|
$hasBg = true;
|
|
61
72
|
}
|
|
62
73
|
if (d.foregroundColorRGB) {
|
|
63
|
-
$fg = d.foregroundColorRGB.rgba >> 8 &
|
|
74
|
+
$fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
|
|
64
75
|
$hasFg = true;
|
|
65
76
|
}
|
|
66
77
|
});
|
|
@@ -68,10 +79,94 @@ export class CellColorResolver {
|
|
|
68
79
|
// Apply the selection color if needed
|
|
69
80
|
$isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y);
|
|
70
81
|
if ($isSelected) {
|
|
71
|
-
|
|
82
|
+
// If the cell has a bg color, retain the color by blending it with the selection color
|
|
83
|
+
if (
|
|
84
|
+
(this.result.fg & FgFlags.INVERSE) ||
|
|
85
|
+
(this.result.bg & Attributes.CM_MASK) !== Attributes.CM_DEFAULT
|
|
86
|
+
) {
|
|
87
|
+
// Resolve the standard bg color
|
|
88
|
+
if (this.result.fg & FgFlags.INVERSE) {
|
|
89
|
+
switch (this.result.fg & Attributes.CM_MASK) {
|
|
90
|
+
case Attributes.CM_P16:
|
|
91
|
+
case Attributes.CM_P256:
|
|
92
|
+
$bg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba;
|
|
93
|
+
break;
|
|
94
|
+
case Attributes.CM_RGB:
|
|
95
|
+
$bg = ((this.result.fg & Attributes.RGB_MASK) << 8) | 0xFF;
|
|
96
|
+
break;
|
|
97
|
+
case Attributes.CM_DEFAULT:
|
|
98
|
+
default:
|
|
99
|
+
$bg = this._themeService.colors.foreground.rgba;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
switch (this.result.bg & Attributes.CM_MASK) {
|
|
103
|
+
case Attributes.CM_P16:
|
|
104
|
+
case Attributes.CM_P256:
|
|
105
|
+
$bg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba;
|
|
106
|
+
break;
|
|
107
|
+
case Attributes.CM_RGB:
|
|
108
|
+
$bg = ((this.result.bg & Attributes.RGB_MASK) << 8) | 0xFF;
|
|
109
|
+
break;
|
|
110
|
+
// No need to consider default bg color here as it's not possible
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Blend with selection bg color
|
|
114
|
+
$bg = rgba.blend(
|
|
115
|
+
$bg,
|
|
116
|
+
((this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba & 0xFFFFFF00) | 0x80
|
|
117
|
+
) >> 8 & Attributes.RGB_MASK;
|
|
118
|
+
} else {
|
|
119
|
+
$bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & Attributes.RGB_MASK;
|
|
120
|
+
}
|
|
72
121
|
$hasBg = true;
|
|
122
|
+
|
|
123
|
+
// Apply explicit selection foreground if present
|
|
73
124
|
if ($colors.selectionForeground) {
|
|
74
|
-
$fg = $colors.selectionForeground.rgba >> 8 &
|
|
125
|
+
$fg = $colors.selectionForeground.rgba >> 8 & Attributes.RGB_MASK;
|
|
126
|
+
$hasFg = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Overwrite fg as bg if it's a special decorative glyph (eg. powerline)
|
|
130
|
+
if (treatGlyphAsBackgroundColor(cell.getCode())) {
|
|
131
|
+
// Inverse default background should be treated as transparent
|
|
132
|
+
if (
|
|
133
|
+
(this.result.fg & FgFlags.INVERSE) &&
|
|
134
|
+
(this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT
|
|
135
|
+
) {
|
|
136
|
+
$fg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & Attributes.RGB_MASK;
|
|
137
|
+
} else {
|
|
138
|
+
|
|
139
|
+
if (this.result.fg & FgFlags.INVERSE) {
|
|
140
|
+
switch (this.result.bg & Attributes.CM_MASK) {
|
|
141
|
+
case Attributes.CM_P16:
|
|
142
|
+
case Attributes.CM_P256:
|
|
143
|
+
$fg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba;
|
|
144
|
+
break;
|
|
145
|
+
case Attributes.CM_RGB:
|
|
146
|
+
$fg = ((this.result.bg & Attributes.RGB_MASK) << 8) | 0xFF;
|
|
147
|
+
break;
|
|
148
|
+
// No need to consider default bg color here as it's not possible
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
switch (this.result.fg & Attributes.CM_MASK) {
|
|
152
|
+
case Attributes.CM_P16:
|
|
153
|
+
case Attributes.CM_P256:
|
|
154
|
+
$fg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba;
|
|
155
|
+
break;
|
|
156
|
+
case Attributes.CM_RGB:
|
|
157
|
+
$fg = ((this.result.fg & Attributes.RGB_MASK) << 8) | 0xFF;
|
|
158
|
+
break;
|
|
159
|
+
case Attributes.CM_DEFAULT:
|
|
160
|
+
default:
|
|
161
|
+
$fg = this._themeService.colors.foreground.rgba;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
$fg = rgba.blend(
|
|
166
|
+
$fg,
|
|
167
|
+
((this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba & 0xFFFFFF00) | 0x80
|
|
168
|
+
) >> 8 & Attributes.RGB_MASK;
|
|
169
|
+
}
|
|
75
170
|
$hasFg = true;
|
|
76
171
|
}
|
|
77
172
|
}
|
|
@@ -79,11 +174,11 @@ export class CellColorResolver {
|
|
|
79
174
|
// Apply decorations on the top layer
|
|
80
175
|
this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
|
|
81
176
|
if (d.backgroundColorRGB) {
|
|
82
|
-
$bg = d.backgroundColorRGB.rgba >> 8 &
|
|
177
|
+
$bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
|
|
83
178
|
$hasBg = true;
|
|
84
179
|
}
|
|
85
180
|
if (d.foregroundColorRGB) {
|
|
86
|
-
$fg = d.foregroundColorRGB.rgba >> 8 &
|
|
181
|
+
$fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
|
|
87
182
|
$hasFg = true;
|
|
88
183
|
}
|
|
89
184
|
});
|
|
@@ -110,7 +205,7 @@ export class CellColorResolver {
|
|
|
110
205
|
if ($hasBg && !$hasFg) {
|
|
111
206
|
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
|
112
207
|
if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
|
113
|
-
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 &
|
|
208
|
+
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
|
114
209
|
} else {
|
|
115
210
|
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
|
116
211
|
}
|
|
@@ -119,7 +214,7 @@ export class CellColorResolver {
|
|
|
119
214
|
if (!$hasBg && $hasFg) {
|
|
120
215
|
// Resolve bg color type (default color has a different meaning in fg vs bg)
|
|
121
216
|
if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
|
|
122
|
-
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 &
|
|
217
|
+
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB;
|
|
123
218
|
} else {
|
|
124
219
|
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
|
|
125
220
|
}
|
|
@@ -133,5 +228,9 @@ export class CellColorResolver {
|
|
|
133
228
|
// Use the override if it exists
|
|
134
229
|
this.result.bg = $hasBg ? $bg : this.result.bg;
|
|
135
230
|
this.result.fg = $hasFg ? $fg : this.result.fg;
|
|
231
|
+
|
|
232
|
+
// Reset overrides variantOffset
|
|
233
|
+
this.result.ext &= ~ExtFlags.VARIANT_OFFSET;
|
|
234
|
+
this.result.ext |= ($variantOffset << 29) & ExtFlags.VARIANT_OFFSET;
|
|
136
235
|
}
|
|
137
236
|
}
|
|
@@ -27,7 +27,7 @@ function isBoxOrBlockGlyph(codepoint: number): boolean {
|
|
|
27
27
|
return 0x2500 <= codepoint && codepoint <= 0x259F;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export function
|
|
30
|
+
export function treatGlyphAsBackgroundColor(codepoint: number): boolean {
|
|
31
31
|
return isPowerlineGlyph(codepoint) || isBoxOrBlockGlyph(codepoint);
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -56,3 +56,7 @@ function createDimension(): IDimensions {
|
|
|
56
56
|
height: 0
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
export function computeNextVariantOffset(cellWidth: number, lineWidth: number, currentOffset: number = 0): number {
|
|
61
|
+
return (cellWidth - (Math.round(lineWidth) * 2 - currentOffset)) % (Math.round(lineWidth) * 2);
|
|
62
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @license MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { ITerminal } from 'browser/Types';
|
|
6
7
|
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
7
8
|
import { Terminal } from '@xterm/xterm';
|
|
8
9
|
|
|
@@ -35,7 +36,7 @@ class SelectionRenderModel implements ISelectionRenderModel {
|
|
|
35
36
|
this.selectionEnd = undefined;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
public update(terminal:
|
|
39
|
+
public update(terminal: ITerminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
|
|
39
40
|
this.selectionStart = start;
|
|
40
41
|
this.selectionEnd = end;
|
|
41
42
|
// Selection does not exist
|
|
@@ -45,8 +46,9 @@ class SelectionRenderModel implements ISelectionRenderModel {
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// Translate from buffer position to viewport position
|
|
48
|
-
const
|
|
49
|
-
const
|
|
49
|
+
const viewportY = terminal.buffers.active.ydisp;
|
|
50
|
+
const viewportStartRow = start[1] - viewportY;
|
|
51
|
+
const viewportEndRow = end[1] - viewportY;
|
|
50
52
|
const viewportCappedStartRow = Math.max(viewportStartRow, 0);
|
|
51
53
|
const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1);
|
|
52
54
|
|