@xterm/xterm 5.4.0-beta.7 → 5.4.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xterm/xterm",
3
3
  "description": "Full xterm terminal, in your browser",
4
- "version": "5.4.0-beta.7",
4
+ "version": "5.4.0-beta.9",
5
5
  "main": "lib/xterm.js",
6
6
  "style": "css/xterm.css",
7
7
  "types": "typings/xterm.d.ts",
@@ -575,7 +575,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
575
575
  }
576
576
 
577
577
  private _createRenderer(): IRenderer {
578
- return this._instantiationService.createInstance(DomRenderer, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2);
578
+ return this._instantiationService.createInstance(DomRenderer, this, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2);
579
579
  }
580
580
 
581
581
  /**
@@ -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 { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
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 = start[1] - this._bufferService.buffer.ydisp;
325
- const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
326
- const viewportCappedStartRow = Math.max(viewportStartRow, 0);
327
- const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
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 = `${colStart * this.dimensions.css.cell.width}px`;
371
- element.style.width = `${this.dimensions.css.cell.width * (colEnd - colStart)}px`;
381
+ element.style.left = `${left}px`;
382
+ element.style.width = `${width}px`;
372
383
  return element;
373
384
  }
374
385
 
@@ -1,8 +1,8 @@
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
8
 
@@ -13,6 +13,7 @@ let $hasFg = false;
13
13
  let $hasBg = false;
14
14
  let $isSelected = false;
15
15
  let $colors: ReadonlyColorSet | undefined;
16
+ let $variantOffset = 0;
16
17
 
17
18
  export class CellColorResolver {
18
19
  /**
@@ -27,6 +28,7 @@ export class CellColorResolver {
27
28
 
28
29
  constructor(
29
30
  private readonly _terminal: Terminal,
31
+ private readonly _optionService: IOptionsService,
30
32
  private readonly _selectionRenderModel: ISelectionRenderModel,
31
33
  private readonly _decorationService: IDecorationService,
32
34
  private readonly _coreBrowserService: ICoreBrowserService,
@@ -38,7 +40,7 @@ export class CellColorResolver {
38
40
  * Resolves colors for the cell, putting the result into the shared {@link result}. This resolves
39
41
  * overrides, inverse and selection for the cell which can then be used to feed into the renderer.
40
42
  */
41
- public resolve(cell: ICellData, x: number, y: number): void {
43
+ public resolve(cell: ICellData, x: number, y: number, deviceCellWidth: number): void {
42
44
  this.result.bg = cell.bg;
43
45
  this.result.fg = cell.fg;
44
46
  this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
@@ -52,6 +54,13 @@ export class CellColorResolver {
52
54
  $hasFg = false;
53
55
  $isSelected = false;
54
56
  $colors = this._themeService.colors;
57
+ $variantOffset = 0;
58
+
59
+ const code = cell.getCode();
60
+ if (code !== NULL_CELL_CODE && cell.extended.underlineStyle === UnderlineStyle.DOTTED) {
61
+ const lineWidth = Math.max(1, Math.floor(this._optionService.rawOptions.fontSize * this._coreBrowserService.dpr / 15));
62
+ $variantOffset = x * deviceCellWidth % (Math.round(lineWidth) * 2);
63
+ }
55
64
 
56
65
  // Apply decorations on the bottom layer
57
66
  this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
@@ -133,5 +142,9 @@ export class CellColorResolver {
133
142
  // Use the override if it exists
134
143
  this.result.bg = $hasBg ? $bg : this.result.bg;
135
144
  this.result.fg = $hasFg ? $fg : this.result.fg;
145
+
146
+ // Reset overrides variantOffset
147
+ this.result.ext &= ~ExtFlags.VARIANT_OFFSET;
148
+ this.result.ext |= ($variantOffset << 29) & ExtFlags.VARIANT_OFFSET;
136
149
  }
137
150
  }
@@ -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: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
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 viewportStartRow = start[1] - terminal.buffer.active.viewportY;
49
- const viewportEndRow = end[1] - terminal.buffer.active.viewportY;
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
 
@@ -6,7 +6,7 @@
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 { excludeFromContrastRatioDemands, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
9
+ import { computeNextVariantOffset, excludeFromContrastRatioDemands, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
10
10
  import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from 'browser/renderer/shared/Types';
11
11
  import { NULL_COLOR, color, rgba } from 'common/Color';
12
12
  import { EventEmitter } from 'common/EventEmitter';
@@ -545,6 +545,7 @@ export class TextureAtlas implements ITextureAtlas {
545
545
  const yTop = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
546
546
  const yMid = yTop + lineWidth;
547
547
  const yBot = yTop + lineWidth * 2;
548
+ let nextOffset = this._workAttributeData.getUnderlineVariantOffset();
548
549
 
549
550
  for (let i = 0; i < chWidth; i++) {
550
551
  this._tmpCtx.save();
@@ -594,9 +595,22 @@ export class TextureAtlas implements ITextureAtlas {
594
595
  );
595
596
  break;
596
597
  case UnderlineStyle.DOTTED:
597
- this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
598
- this._tmpCtx.moveTo(xChLeft, yTop);
599
- this._tmpCtx.lineTo(xChRight, yTop);
598
+ const offsetWidth = nextOffset === 0 ? 0 :
599
+ (nextOffset >= lineWidth ? lineWidth * 2 - nextOffset : lineWidth - nextOffset);
600
+ // a line and a gap.
601
+ const isLineStart = nextOffset >= lineWidth ? false : true;
602
+ if (isLineStart === false || offsetWidth === 0) {
603
+ this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
604
+ this._tmpCtx.moveTo(xChLeft + offsetWidth, yTop);
605
+ this._tmpCtx.lineTo(xChRight, yTop);
606
+ } else {
607
+ this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
608
+ this._tmpCtx.moveTo(xChLeft, yTop);
609
+ this._tmpCtx.lineTo(xChLeft + offsetWidth, yTop);
610
+ this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTop);
611
+ this._tmpCtx.lineTo(xChRight, yTop);
612
+ }
613
+ nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset);
600
614
  break;
601
615
  case UnderlineStyle.DASHED:
602
616
  this._tmpCtx.setLineDash([this._config.devicePixelRatio * 4, this._config.devicePixelRatio * 3]);
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { FontWeight, Terminal } from '@xterm/xterm';
7
- import { IColorSet } from 'browser/Types';
7
+ import { IColorSet, ITerminal } from 'browser/Types';
8
8
  import { IDisposable } from 'common/Types';
9
9
  import { IEvent } from 'common/EventEmitter';
10
10
 
@@ -168,6 +168,6 @@ export interface ISelectionRenderModel {
168
168
  readonly selectionStart: [number, number] | undefined;
169
169
  readonly selectionEnd: [number, number] | undefined;
170
170
  clear(): void;
171
- update(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
171
+ update(terminal: ITerminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode?: boolean): void;
172
172
  isCellSelected(terminal: Terminal, x: number, y: number): boolean;
173
173
  }
@@ -119,6 +119,7 @@ export interface IExtendedAttrs {
119
119
  ext: number;
120
120
  underlineStyle: UnderlineStyle;
121
121
  underlineColor: number;
122
+ underlineVariantOffset: number;
122
123
  urlId: number;
123
124
  clone(): IExtendedAttrs;
124
125
  isEmpty(): boolean;
@@ -209,6 +210,7 @@ export interface IAttributeData {
209
210
  isUnderlineColorPalette(): boolean;
210
211
  isUnderlineColorDefault(): boolean;
211
212
  getUnderlineStyle(): number;
213
+ getUnderlineVariantOffset(): number;
212
214
  }
213
215
 
214
216
  /** Cell data */
@@ -126,6 +126,9 @@ export class AttributeData implements IAttributeData {
126
126
  ? (this.bg & BgFlags.HAS_EXTENDED ? this.extended.underlineStyle : UnderlineStyle.SINGLE)
127
127
  : UnderlineStyle.NONE;
128
128
  }
129
+ public getUnderlineVariantOffset(): number {
130
+ return this.extended.underlineVariantOffset;
131
+ }
129
132
  }
130
133
 
131
134
 
@@ -174,6 +177,18 @@ export class ExtendedAttrs implements IExtendedAttrs {
174
177
  this._urlId = value;
175
178
  }
176
179
 
180
+ public get underlineVariantOffset(): number {
181
+ const val = (this._ext & ExtFlags.VARIANT_OFFSET) >> 29;
182
+ if (val < 0) {
183
+ return val ^ 0xFFFFFFF8;
184
+ }
185
+ return val;
186
+ }
187
+ public set underlineVariantOffset(value: number) {
188
+ this._ext &= ~ExtFlags.VARIANT_OFFSET;
189
+ this._ext |= (value << 29) & ExtFlags.VARIANT_OFFSET;
190
+ }
191
+
177
192
  constructor(
178
193
  ext: number = 0,
179
194
  urlId: number = 0
@@ -134,9 +134,17 @@ export const enum BgFlags {
134
134
 
135
135
  export const enum ExtFlags {
136
136
  /**
137
- * bit 27..32 (upper 3 unused)
137
+ * bit 27..29
138
138
  */
139
- UNDERLINE_STYLE = 0x1C000000
139
+ UNDERLINE_STYLE = 0x1C000000,
140
+
141
+ /**
142
+ * bit 30..32
143
+ *
144
+ * An optional variant for the glyph, this can be used for example to offset underlines by a
145
+ * number of pixels to create a perfect pattern.
146
+ */
147
+ VARIANT_OFFSET = 0xE0000000
140
148
  }
141
149
 
142
150
  export const enum UnderlineStyle {