@xterm/xterm 5.4.0-beta.9 → 5.5.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 { Linkifier2 } from 'browser/Linkifier2';
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 { color, rgba } from 'common/Color';
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
- // browser services
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 channels = color.toColorRGB(acc === 'ansi'
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(channels)}${C1_ESCAPED.ST}`);
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] = rgba.toColor(...req.color));
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] = rgba.toColor(...req.color));
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: KeyboardEvent): void {
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: KeyboardEvent) => this._handleTextAreaFocus(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.linkifier2
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, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2);
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.linkifier2.registerLinkProvider(linkProvider);
903
+ return this._linkProviderService.registerLinkProvider(linkProvider);
896
904
  }
897
905
 
898
906
  public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
@@ -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 {
@@ -138,6 +138,9 @@ export class Terminal extends Disposable implements ITerminalApi {
138
138
  public focus(): void {
139
139
  this._core.focus();
140
140
  }
141
+ public input(data: string, wasUserInput: boolean = true): void {
142
+ this._core.input(data, wasUserInput);
143
+ }
141
144
  public resize(columns: number, rows: number): void {
142
145
  this._verifyIntegers(columns, rows);
143
146
  this._core.resize(columns, rows);
@@ -148,6 +151,9 @@ export class Terminal extends Disposable implements ITerminalApi {
148
151
  public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
149
152
  this._core.attachCustomKeyEventHandler(customKeyEventHandler);
150
153
  }
154
+ public attachCustomWheelEventHandler(customWheelEventHandler: (event: WheelEvent) => boolean): void {
155
+ this._core.attachCustomWheelEventHandler(customWheelEventHandler);
156
+ }
151
157
  public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
152
158
  return this._core.registerLinkProvider(linkProvider);
153
159
  }
@@ -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 { color, rgba } from 'common/Color';
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 { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils';
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 = rgba.toColor(bg >> 16, bg >> 8 & 0xFF, bg & 0xFF);
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 = rgba.toColor(
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 || excludeFromContrastRatioDemands(cell.getCode())) {
461
+ if (this._optionsService.rawOptions.minimumContrastRatio === 1 || treatGlyphAsBackgroundColor(cell.getCode())) {
462
462
  return false;
463
463
  }
464
464
 
@@ -134,9 +134,14 @@ export class WidthCache implements IDisposable {
134
134
  public get(c: string, bold: boolean | number, italic: boolean | number): number {
135
135
  let cp = 0;
136
136
  if (!bold && !italic && c.length === 1 && (cp = c.charCodeAt(0)) < WidthCacheSettings.FLAT_SIZE) {
137
- return this._flat[cp] !== WidthCacheSettings.FLAT_UNSET
138
- ? this._flat[cp]
139
- : (this._flat[cp] = this._measure(c, 0));
137
+ if (this._flat[cp] !== WidthCacheSettings.FLAT_UNSET) {
138
+ return this._flat[cp];
139
+ }
140
+ const width = this._measure(c, 0);
141
+ if (width > 0) {
142
+ this._flat[cp] = width;
143
+ }
144
+ return width;
140
145
  }
141
146
  let key = c;
142
147
  if (bold) key += 'B';
@@ -147,7 +152,9 @@ export class WidthCache implements IDisposable {
147
152
  if (bold) variant |= FontVariant.BOLD;
148
153
  if (italic) variant |= FontVariant.ITALIC;
149
154
  width = this._measure(c, variant);
150
- this._holey!.set(key, width);
155
+ if (width > 0) {
156
+ this._holey!.set(key, width);
157
+ }
151
158
  }
152
159
  return width;
153
160
  }
@@ -5,6 +5,8 @@ import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle
5
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;
@@ -65,11 +67,11 @@ export class CellColorResolver {
65
67
  // Apply decorations on the bottom layer
66
68
  this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
67
69
  if (d.backgroundColorRGB) {
68
- $bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
70
+ $bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
69
71
  $hasBg = true;
70
72
  }
71
73
  if (d.foregroundColorRGB) {
72
- $fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
74
+ $fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
73
75
  $hasFg = true;
74
76
  }
75
77
  });
@@ -77,10 +79,94 @@ export class CellColorResolver {
77
79
  // Apply the selection color if needed
78
80
  $isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y);
79
81
  if ($isSelected) {
80
- $bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
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
+ }
81
121
  $hasBg = true;
122
+
123
+ // Apply explicit selection foreground if present
82
124
  if ($colors.selectionForeground) {
83
- $fg = $colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
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
+ }
84
170
  $hasFg = true;
85
171
  }
86
172
  }
@@ -88,11 +174,11 @@ export class CellColorResolver {
88
174
  // Apply decorations on the top layer
89
175
  this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
90
176
  if (d.backgroundColorRGB) {
91
- $bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
177
+ $bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
92
178
  $hasBg = true;
93
179
  }
94
180
  if (d.foregroundColorRGB) {
95
- $fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
181
+ $fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
96
182
  $hasFg = true;
97
183
  }
98
184
  });
@@ -119,7 +205,7 @@ export class CellColorResolver {
119
205
  if ($hasBg && !$hasFg) {
120
206
  // Resolve bg color type (default color has a different meaning in fg vs bg)
121
207
  if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
122
- $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
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;
123
209
  } else {
124
210
  $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
125
211
  }
@@ -128,7 +214,7 @@ export class CellColorResolver {
128
214
  if (!$hasBg && $hasFg) {
129
215
  // Resolve bg color type (default color has a different meaning in fg vs bg)
130
216
  if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
131
- $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
217
+ $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB;
132
218
  } else {
133
219
  $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
134
220
  }
@@ -27,7 +27,7 @@ function isBoxOrBlockGlyph(codepoint: number): boolean {
27
27
  return 0x2500 <= codepoint && codepoint <= 0x259F;
28
28
  }
29
29
 
30
- export function excludeFromContrastRatioDemands(codepoint: number): boolean {
30
+ export function treatGlyphAsBackgroundColor(codepoint: number): boolean {
31
31
  return isPowerlineGlyph(codepoint) || isBoxOrBlockGlyph(codepoint);
32
32
  }
33
33
 
@@ -6,16 +6,15 @@
6
6
  import { IColorContrastCache } from 'browser/Types';
7
7
  import { DIM_OPACITY, TEXT_BASELINE } from 'browser/renderer/shared/Constants';
8
8
  import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs';
9
- import { computeNextVariantOffset, excludeFromContrastRatioDemands, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
9
+ import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
10
10
  import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from 'browser/renderer/shared/Types';
11
- import { NULL_COLOR, color, rgba } from 'common/Color';
11
+ import { NULL_COLOR, channels, color, rgba } from 'common/Color';
12
12
  import { EventEmitter } from 'common/EventEmitter';
13
13
  import { FourKeyMap } from 'common/MultiKeyMap';
14
14
  import { IdleTaskQueue } from 'common/TaskQueue';
15
15
  import { IColor } from 'common/Types';
16
16
  import { AttributeData } from 'common/buffer/AttributeData';
17
17
  import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants';
18
- import { traceCall } from 'common/services/LogService';
19
18
  import { IUnicodeService } from 'common/services/Services';
20
19
 
21
20
  /**
@@ -292,8 +291,7 @@ export class TextureAtlas implements ITextureAtlas {
292
291
  break;
293
292
  case Attributes.CM_RGB:
294
293
  const arr = AttributeData.toColorRGB(bgColor);
295
- // TODO: This object creation is slow
296
- result = rgba.toColor(arr[0], arr[1], arr[2]);
294
+ result = channels.toColor(arr[0], arr[1], arr[2]);
297
295
  break;
298
296
  case Attributes.CM_DEFAULT:
299
297
  default:
@@ -325,7 +323,7 @@ export class TextureAtlas implements ITextureAtlas {
325
323
  break;
326
324
  case Attributes.CM_RGB:
327
325
  const arr = AttributeData.toColorRGB(fgColor);
328
- result = rgba.toColor(arr[0], arr[1], arr[2]);
326
+ result = channels.toColor(arr[0], arr[1], arr[2]);
329
327
  break;
330
328
  case Attributes.CM_DEFAULT:
331
329
  default:
@@ -407,7 +405,7 @@ export class TextureAtlas implements ITextureAtlas {
407
405
  return undefined;
408
406
  }
409
407
 
410
- const color = rgba.toColor(
408
+ const color = channels.toColor(
411
409
  (result >> 24) & 0xFF,
412
410
  (result >> 16) & 0xFF,
413
411
  (result >> 8) & 0xFF
@@ -424,7 +422,6 @@ export class TextureAtlas implements ITextureAtlas {
424
422
  return this._config.colors.contrastCache;
425
423
  }
426
424
 
427
- @traceCall
428
425
  private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean = false): IRasterizedGlyph {
429
426
  const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars;
430
427
 
@@ -492,7 +489,7 @@ export class TextureAtlas implements ITextureAtlas {
492
489
 
493
490
  const powerlineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0));
494
491
  const restrictedPowerlineGlyph = chars.length === 1 && isRestrictedPowerlineGlyph(chars.charCodeAt(0));
495
- const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, excludeFromContrastRatioDemands(chars.charCodeAt(0)));
492
+ const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, treatGlyphAsBackgroundColor(chars.charCodeAt(0)));
496
493
  this._tmpCtx.fillStyle = foregroundColor.css;
497
494
 
498
495
  // For powerline glyphs left/top padding is excluded (https://github.com/microsoft/vscode/issues/120129)
@@ -613,7 +610,14 @@ export class TextureAtlas implements ITextureAtlas {
613
610
  nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset);
614
611
  break;
615
612
  case UnderlineStyle.DASHED:
616
- this._tmpCtx.setLineDash([this._config.devicePixelRatio * 4, this._config.devicePixelRatio * 3]);
613
+ const lineRatio = 0.6;
614
+ const gapRatio = 0.3;
615
+ // End line ratio is approximately equal to 0.1
616
+ const xChWidth = xChRight - xChLeft;
617
+ const line = Math.floor(lineRatio * xChWidth);
618
+ const gap = Math.floor(gapRatio * xChWidth);
619
+ const end = xChWidth - line - gap;
620
+ this._tmpCtx.setLineDash([line, gap, end]);
617
621
  this._tmpCtx.moveTo(xChLeft, yTop);
618
622
  this._tmpCtx.lineTo(xChRight, yTop);
619
623
  break;