@xterm/xterm 5.4.0-beta.1 → 5.4.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/package.json +1 -1
- package/src/browser/Terminal.ts +1 -1
- package/src/browser/renderer/dom/DomRenderer.ts +20 -9
- package/src/browser/renderer/shared/CellColorResolver.ts +16 -3
- package/src/browser/renderer/shared/RendererUtils.ts +4 -0
- package/src/browser/renderer/shared/SelectionRenderModel.ts +5 -3
- package/src/browser/renderer/shared/TextureAtlas.ts +18 -4
- package/src/browser/renderer/shared/Types.d.ts +2 -2
- package/src/common/InputHandler.ts +15 -19
- package/src/common/Types.d.ts +6 -4
- package/src/common/buffer/AttributeData.ts +15 -0
- package/src/common/buffer/BufferLine.ts +17 -17
- package/src/common/buffer/Constants.ts +10 -2
- package/src/common/input/Keyboard.ts +2 -3
package/package.json
CHANGED
package/src/browser/Terminal.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
|
@@ -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:
|
|
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
|
|
|
@@ -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
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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:
|
|
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
|
}
|
|
@@ -519,7 +519,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
519
519
|
|
|
520
520
|
// handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char
|
|
521
521
|
if (this._activeBuffer.x && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x - 1) === 2) {
|
|
522
|
-
bufferRow.
|
|
522
|
+
bufferRow.setCellFromCodepoint(this._activeBuffer.x - 1, 0, 1, curAttr);
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
let precedingJoinState = this._parser.precedingJoinState;
|
|
@@ -581,7 +581,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
581
581
|
}
|
|
582
582
|
// clear left over cells to the right
|
|
583
583
|
while (oldCol < cols) {
|
|
584
|
-
oldRow.
|
|
584
|
+
oldRow.setCellFromCodepoint(oldCol++, 0, 1, curAttr);
|
|
585
585
|
}
|
|
586
586
|
} else {
|
|
587
587
|
this._activeBuffer.x = cols - 1;
|
|
@@ -605,7 +605,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
605
605
|
bufferRow.addCodepointToCell(this._activeBuffer.x - offset,
|
|
606
606
|
code, chWidth);
|
|
607
607
|
for (let delta = chWidth - oldWidth; --delta >= 0; ) {
|
|
608
|
-
bufferRow.
|
|
608
|
+
bufferRow.setCellFromCodepoint(this._activeBuffer.x++, 0, 0, curAttr);
|
|
609
609
|
}
|
|
610
610
|
continue;
|
|
611
611
|
}
|
|
@@ -613,17 +613,17 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
613
613
|
// insert mode: move characters to right
|
|
614
614
|
if (insertMode) {
|
|
615
615
|
// right shift cells according to the width
|
|
616
|
-
bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(curAttr)
|
|
616
|
+
bufferRow.insertCells(this._activeBuffer.x, chWidth - oldWidth, this._activeBuffer.getNullCell(curAttr));
|
|
617
617
|
// test last cell - since the last cell has only room for
|
|
618
618
|
// a halfwidth char any fullwidth shifted there is lost
|
|
619
619
|
// and will be set to empty cell
|
|
620
620
|
if (bufferRow.getWidth(cols - 1) === 2) {
|
|
621
|
-
bufferRow.
|
|
621
|
+
bufferRow.setCellFromCodepoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr);
|
|
622
622
|
}
|
|
623
623
|
}
|
|
624
624
|
|
|
625
625
|
// write current char to buffer and advance cursor
|
|
626
|
-
bufferRow.
|
|
626
|
+
bufferRow.setCellFromCodepoint(this._activeBuffer.x++, code, chWidth, curAttr);
|
|
627
627
|
|
|
628
628
|
// fullwidth char - also set next cell to placeholder stub and advance cursor
|
|
629
629
|
// for graphemes bigger than fullwidth we can simply loop to zero
|
|
@@ -631,7 +631,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
631
631
|
if (chWidth > 0) {
|
|
632
632
|
while (--chWidth) {
|
|
633
633
|
// other than a regular empty cell a cell following a wide char has no width
|
|
634
|
-
bufferRow.
|
|
634
|
+
bufferRow.setCellFromCodepoint(this._activeBuffer.x++, 0, 0, curAttr);
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
637
|
}
|
|
@@ -640,7 +640,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
640
640
|
|
|
641
641
|
// handle wide chars: reset cell to the right if it is second cell of a wide char
|
|
642
642
|
if (this._activeBuffer.x < cols && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x) === 0 && !bufferRow.hasContent(this._activeBuffer.x)) {
|
|
643
|
-
bufferRow.
|
|
643
|
+
bufferRow.setCellFromCodepoint(this._activeBuffer.x, 0, 1, curAttr);
|
|
644
644
|
}
|
|
645
645
|
|
|
646
646
|
this._dirtyRowTracker.markDirty(this._activeBuffer.y);
|
|
@@ -1145,7 +1145,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1145
1145
|
start,
|
|
1146
1146
|
end,
|
|
1147
1147
|
this._activeBuffer.getNullCell(this._eraseAttrData()),
|
|
1148
|
-
this._eraseAttrData(),
|
|
1149
1148
|
respectProtect
|
|
1150
1149
|
);
|
|
1151
1150
|
if (clearWrap) {
|
|
@@ -1366,8 +1365,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1366
1365
|
line.insertCells(
|
|
1367
1366
|
this._activeBuffer.x,
|
|
1368
1367
|
params.params[0] || 1,
|
|
1369
|
-
this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1370
|
-
this._eraseAttrData()
|
|
1368
|
+
this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1371
1369
|
);
|
|
1372
1370
|
this._dirtyRowTracker.markDirty(this._activeBuffer.y);
|
|
1373
1371
|
}
|
|
@@ -1393,8 +1391,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1393
1391
|
line.deleteCells(
|
|
1394
1392
|
this._activeBuffer.x,
|
|
1395
1393
|
params.params[0] || 1,
|
|
1396
|
-
this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1397
|
-
this._eraseAttrData()
|
|
1394
|
+
this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1398
1395
|
);
|
|
1399
1396
|
this._dirtyRowTracker.markDirty(this._activeBuffer.y);
|
|
1400
1397
|
}
|
|
@@ -1461,7 +1458,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1461
1458
|
const param = params.params[0] || 1;
|
|
1462
1459
|
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
|
1463
1460
|
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
|
1464
|
-
line.deleteCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1461
|
+
line.deleteCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
|
|
1465
1462
|
line.isWrapped = false;
|
|
1466
1463
|
}
|
|
1467
1464
|
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
|
@@ -1494,7 +1491,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1494
1491
|
const param = params.params[0] || 1;
|
|
1495
1492
|
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
|
1496
1493
|
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
|
1497
|
-
line.insertCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1494
|
+
line.insertCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
|
|
1498
1495
|
line.isWrapped = false;
|
|
1499
1496
|
}
|
|
1500
1497
|
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
|
@@ -1517,7 +1514,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1517
1514
|
const param = params.params[0] || 1;
|
|
1518
1515
|
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
|
1519
1516
|
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
|
1520
|
-
line.insertCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1517
|
+
line.insertCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
|
|
1521
1518
|
line.isWrapped = false;
|
|
1522
1519
|
}
|
|
1523
1520
|
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
|
@@ -1540,7 +1537,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1540
1537
|
const param = params.params[0] || 1;
|
|
1541
1538
|
for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
|
|
1542
1539
|
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
|
|
1543
|
-
line.deleteCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1540
|
+
line.deleteCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()));
|
|
1544
1541
|
line.isWrapped = false;
|
|
1545
1542
|
}
|
|
1546
1543
|
this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
|
|
@@ -1562,8 +1559,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
1562
1559
|
line.replaceCells(
|
|
1563
1560
|
this._activeBuffer.x,
|
|
1564
1561
|
this._activeBuffer.x + (params.params[0] || 1),
|
|
1565
|
-
this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1566
|
-
this._eraseAttrData()
|
|
1562
|
+
this._activeBuffer.getNullCell(this._eraseAttrData())
|
|
1567
1563
|
);
|
|
1568
1564
|
this._dirtyRowTracker.markDirty(this._activeBuffer.y);
|
|
1569
1565
|
}
|
package/src/common/Types.d.ts
CHANGED
|
@@ -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 */
|
|
@@ -233,11 +235,11 @@ export interface IBufferLine {
|
|
|
233
235
|
set(index: number, value: CharData): void;
|
|
234
236
|
loadCell(index: number, cell: ICellData): ICellData;
|
|
235
237
|
setCell(index: number, cell: ICellData): void;
|
|
236
|
-
|
|
238
|
+
setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void;
|
|
237
239
|
addCodepointToCell(index: number, codePoint: number, width: number): void;
|
|
238
|
-
insertCells(pos: number, n: number, ch: ICellData
|
|
239
|
-
deleteCells(pos: number, n: number, fill: ICellData
|
|
240
|
-
replaceCells(start: number, end: number, fill: ICellData,
|
|
240
|
+
insertCells(pos: number, n: number, ch: ICellData): void;
|
|
241
|
+
deleteCells(pos: number, n: number, fill: ICellData): void;
|
|
242
|
+
replaceCells(start: number, end: number, fill: ICellData, respectProtect?: boolean): void;
|
|
241
243
|
resize(cols: number, fill: ICellData): boolean;
|
|
242
244
|
cleanupMemory(): number;
|
|
243
245
|
fill(fillCellData: ICellData, respectProtect?: boolean): void;
|
|
@@ -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
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { CharData, IAttributeData, IBufferLine, ICellData, IExtendedAttrs } from 'common/Types';
|
|
7
|
-
import { AttributeData
|
|
7
|
+
import { AttributeData } from 'common/buffer/AttributeData';
|
|
8
8
|
import { CellData } from 'common/buffer/CellData';
|
|
9
9
|
import { Attributes, BgFlags, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, Content, NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
|
10
10
|
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
|
@@ -212,13 +212,13 @@ export class BufferLine implements IBufferLine {
|
|
|
212
212
|
* Since the input handler see the incoming chars as UTF32 codepoints,
|
|
213
213
|
* it gets an optimized access method.
|
|
214
214
|
*/
|
|
215
|
-
public
|
|
216
|
-
if (bg & BgFlags.HAS_EXTENDED) {
|
|
217
|
-
this._extendedAttrs[index] =
|
|
215
|
+
public setCellFromCodepoint(index: number, codePoint: number, width: number, attrs: IAttributeData): void {
|
|
216
|
+
if (attrs.bg & BgFlags.HAS_EXTENDED) {
|
|
217
|
+
this._extendedAttrs[index] = attrs.extended;
|
|
218
218
|
}
|
|
219
219
|
this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
|
220
|
-
this._data[index * CELL_SIZE + Cell.FG] = fg;
|
|
221
|
-
this._data[index * CELL_SIZE + Cell.BG] = bg;
|
|
220
|
+
this._data[index * CELL_SIZE + Cell.FG] = attrs.fg;
|
|
221
|
+
this._data[index * CELL_SIZE + Cell.BG] = attrs.bg;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
/**
|
|
@@ -253,12 +253,12 @@ export class BufferLine implements IBufferLine {
|
|
|
253
253
|
this._data[index * CELL_SIZE + Cell.CONTENT] = content;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
public insertCells(pos: number, n: number, fillCellData: ICellData
|
|
256
|
+
public insertCells(pos: number, n: number, fillCellData: ICellData): void {
|
|
257
257
|
pos %= this.length;
|
|
258
258
|
|
|
259
259
|
// handle fullwidth at pos: reset cell one to the left if pos is second cell of a wide char
|
|
260
260
|
if (pos && this.getWidth(pos - 1) === 2) {
|
|
261
|
-
this.
|
|
261
|
+
this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
if (n < this.length - pos) {
|
|
@@ -277,11 +277,11 @@ export class BufferLine implements IBufferLine {
|
|
|
277
277
|
|
|
278
278
|
// handle fullwidth at line end: reset last cell if it is first cell of a wide char
|
|
279
279
|
if (this.getWidth(this.length - 1) === 2) {
|
|
280
|
-
this.
|
|
280
|
+
this.setCellFromCodepoint(this.length - 1, 0, 1, fillCellData);
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
public deleteCells(pos: number, n: number, fillCellData: ICellData
|
|
284
|
+
public deleteCells(pos: number, n: number, fillCellData: ICellData): void {
|
|
285
285
|
pos %= this.length;
|
|
286
286
|
if (n < this.length - pos) {
|
|
287
287
|
const cell = new CellData();
|
|
@@ -301,21 +301,21 @@ export class BufferLine implements IBufferLine {
|
|
|
301
301
|
// - reset pos-1 if wide char
|
|
302
302
|
// - reset pos if width==0 (previous second cell of a wide char)
|
|
303
303
|
if (pos && this.getWidth(pos - 1) === 2) {
|
|
304
|
-
this.
|
|
304
|
+
this.setCellFromCodepoint(pos - 1, 0, 1, fillCellData);
|
|
305
305
|
}
|
|
306
306
|
if (this.getWidth(pos) === 0 && !this.hasContent(pos)) {
|
|
307
|
-
this.
|
|
307
|
+
this.setCellFromCodepoint(pos, 0, 1, fillCellData);
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
public replaceCells(start: number, end: number, fillCellData: ICellData,
|
|
311
|
+
public replaceCells(start: number, end: number, fillCellData: ICellData, respectProtect: boolean = false): void {
|
|
312
312
|
// full branching on respectProtect==true, hopefully getting fast JIT for standard case
|
|
313
313
|
if (respectProtect) {
|
|
314
314
|
if (start && this.getWidth(start - 1) === 2 && !this.isProtected(start - 1)) {
|
|
315
|
-
this.
|
|
315
|
+
this.setCellFromCodepoint(start - 1, 0, 1, fillCellData);
|
|
316
316
|
}
|
|
317
317
|
if (end < this.length && this.getWidth(end - 1) === 2 && !this.isProtected(end)) {
|
|
318
|
-
this.
|
|
318
|
+
this.setCellFromCodepoint(end, 0, 1, fillCellData);
|
|
319
319
|
}
|
|
320
320
|
while (start < end && start < this.length) {
|
|
321
321
|
if (!this.isProtected(start)) {
|
|
@@ -328,11 +328,11 @@ export class BufferLine implements IBufferLine {
|
|
|
328
328
|
|
|
329
329
|
// handle fullwidth at start: reset cell one to the left if start is second cell of a wide char
|
|
330
330
|
if (start && this.getWidth(start - 1) === 2) {
|
|
331
|
-
this.
|
|
331
|
+
this.setCellFromCodepoint(start - 1, 0, 1, fillCellData);
|
|
332
332
|
}
|
|
333
333
|
// handle fullwidth at last cell + 1: reset to empty cell if it is second part of a wide char
|
|
334
334
|
if (end < this.length && this.getWidth(end - 1) === 2) {
|
|
335
|
-
this.
|
|
335
|
+
this.setCellFromCodepoint(end, 0, 1, fillCellData);
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
while (start < end && start < this.length) {
|
|
@@ -134,9 +134,17 @@ export const enum BgFlags {
|
|
|
134
134
|
|
|
135
135
|
export const enum ExtFlags {
|
|
136
136
|
/**
|
|
137
|
-
* bit 27..
|
|
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 {
|
|
@@ -83,11 +83,10 @@ export function evaluateKeyboardEvent(
|
|
|
83
83
|
break;
|
|
84
84
|
case 8:
|
|
85
85
|
// backspace
|
|
86
|
+
result.key = ev.ctrlKey ? '\b' : C0.DEL; // ^H or ^?
|
|
86
87
|
if (ev.altKey) {
|
|
87
|
-
result.key = C0.ESC +
|
|
88
|
-
break;
|
|
88
|
+
result.key = C0.ESC + result.key;
|
|
89
89
|
}
|
|
90
|
-
result.key = C0.DEL; // ^?
|
|
91
90
|
break;
|
|
92
91
|
case 9:
|
|
93
92
|
// tab
|