diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.ts b/addons/xterm-addon-serialize/src/SerializeAddon.ts index c68abab7..66796ecd 100644 --- a/addons/xterm-addon-serialize/src/SerializeAddon.ts +++ b/addons/xterm-addon-serialize/src/SerializeAddon.ts @@ -12,6 +12,7 @@ function crop(value: number | undefined, low: number, high: number, initial: num return Math.max(low, Math.min(value, high)); } +// TODO: Refine this template class later abstract class BaseSerializeHandler { constructor(private _buffer: IBuffer) { } @@ -21,12 +22,12 @@ abstract class BaseSerializeHandler { const cell2 = this._buffer.getNullCell(); let oldCell = cell1; - this._serializeStart(endRow - startRow); + this.serializeStart(endRow - startRow); for (let row = startRow; row < endRow; row++) { const line = this._buffer.getLine(row); - this._lineStart(row); + this.lineStart(row); if (line) { for (let col = 0; col < line.length; col++) { @@ -37,41 +38,41 @@ abstract class BaseSerializeHandler { continue; } if (!newCell.equalFg(oldCell) || !newCell.equalBg(oldCell)) { - this._cellFgBgChanged(newCell, oldCell, row, col); + this.cellFgBgChanged(newCell, oldCell, row, col); } if (!newCell.equalFlags(oldCell)) { - this._cellFlagsChanged(newCell, oldCell, row, col); + this.cellFlagsChanged(newCell, oldCell, row, col); } - this._nextCell(newCell, oldCell, row, col); + this.nextCell(newCell, oldCell, row, col); oldCell = newCell; } } - this._lineEnd(row); + this.lineEnd(row); } - this._serializeEnd(); + this.serializeEnd(); - return this._serializeFinished(); + return this.serializeFinished(); } - protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } + protected nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } - protected _cellFlagsChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } + protected cellFlagsChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } - protected _cellFgBgChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } + protected cellFgBgChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } - protected _lineStart(row: number): void { } + protected lineStart(row: number): void { } - protected _lineEnd(row: number): void { } + protected lineEnd(row: number): void { } - protected _serializeStart(rows: number): void { } + protected serializeStart(rows: number): void { } - protected _serializeEnd(): void { } + protected serializeEnd(): void { } - protected _serializeFinished(): string { return ''; } + protected serializeFinished(): string { return ''; } } class StringSerializeHandler extends BaseSerializeHandler { @@ -84,63 +85,59 @@ class StringSerializeHandler extends BaseSerializeHandler { super(buffer); } - protected _serializeStart(rows: number): void { + protected serializeStart(rows: number): void { this._allRows = new Array(rows); } - protected _lineEnd(row: number): void { + protected lineEnd(row: number): void { this._allRows[this._rowIndex++] = this._currentRow; this._currentRow = ''; } - protected _cellFlagsChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { + protected cellFlagsChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { const sgrSeq = this._sgrSeq; // skip if it's default color style, we will use \x1b[0m to clear every color style later - if (cell.isDefaultAttibutes() || cell.equalFlags(oldCell)) { return; } + if (cell.isAttributeDefault() || cell.equalFlags(oldCell)) { return; } - if (cell.flags.inverse !== oldCell.flags.inverse) { sgrSeq.push(cell.flags.inverse ? 7 : 27); } - if (cell.flags.bold !== oldCell.flags.bold) { sgrSeq.push(cell.flags.bold ? 1 : 22); } - if (cell.flags.underline !== oldCell.flags.underline) { sgrSeq.push(cell.flags.underline ? 4 : 24); } - if (cell.flags.blink !== oldCell.flags.blink) { sgrSeq.push(cell.flags.blink ? 5 : 25); } - if (cell.flags.invisible !== oldCell.flags.invisible) { sgrSeq.push(cell.flags.invisible ? 8 : 28); } - if (cell.flags.italic !== oldCell.flags.italic) { sgrSeq.push(cell.flags.italic ? 3 : 23); } - if (cell.flags.dim !== oldCell.flags.dim) { sgrSeq.push(cell.flags.dim ? 2 : 22); } + if (cell.isInverse() !== oldCell.isInverse()) { sgrSeq.push(cell.isInverse() ? 7 : 27); } + if (cell.isBold() !== oldCell.isBold()) { sgrSeq.push(cell.isBold() ? 1 : 22); } + if (cell.isUnderline() !== oldCell.isUnderline()) { sgrSeq.push(cell.isUnderline() ? 4 : 24); } + if (cell.isBlink() !== oldCell.isBlink()) { sgrSeq.push(cell.isBlink() ? 5 : 25); } + if (cell.isInvisible() !== oldCell.isInvisible()) { sgrSeq.push(cell.isInvisible() ? 8 : 28); } + if (cell.isItalic() !== oldCell.isItalic()) { sgrSeq.push(cell.isItalic() ? 3 : 23); } + if (cell.isDim() !== oldCell.isDim()) { sgrSeq.push(cell.isDim() ? 2 : 22); } } - protected _cellFgBgChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { + protected cellFgBgChanged(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { const sgrSeq = this._sgrSeq; // skip if it's default color style, we will use \x1b[0m to clear every color style later - if (cell.isDefaultAttibutes()) { return; } + if (cell.isAttributeDefault()) { return; } if (!cell.equalFg(oldCell)) { - const color = cell.fg.color; - switch (cell.fg.colorMode) { - case 'RGB': sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); break; - case 'P256': sgrSeq.push(38, 5, color); break; - case 'P16': sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7)); break; - default: sgrSeq.push(39); break; - } + const color = cell.getFgColor(); + if (cell.isFgRGB()) { sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); } + else if (cell.isFgPalette256()) { sgrSeq.push(38, 5, color); } + else if (cell.isFgPalette16()) { sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7)); } + else { sgrSeq.push(39); } } if (!cell.equalBg(oldCell)) { - const color = cell.bg.color; - switch (cell.bg.colorMode) { - case 'RGB': sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); break; - case 'P256': sgrSeq.push(48, 5, color); break; - case 'P16': sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7)); break; - default: sgrSeq.push(49); break; - } + const color = cell.getBgColor(); + if (cell.isBgRGB()) { sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); } + else if (cell.isBgPalette256()) { sgrSeq.push(48, 5, color); } + else if (cell.isBgPalette16()) { sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7)); } + else { sgrSeq.push(49); } } } - protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { + protected nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { const fgChanged = !cell.equalFg(oldCell); const bgChanged = !cell.equalBg(oldCell); const flagsChanged = !cell.equalFlags(oldCell); - if (cell.isDefaultAttibutes() && (fgChanged || bgChanged || flagsChanged)) { + if (cell.isAttributeDefault() && (fgChanged || bgChanged || flagsChanged)) { this._currentRow += '\x1b[0m'; } @@ -152,7 +149,7 @@ class StringSerializeHandler extends BaseSerializeHandler { this._currentRow += cell.char; } - protected _serializeFinished(): string { + protected serializeFinished(): string { let rowEnd = this._allRows.length; for (; rowEnd > 0; rowEnd--) { diff --git a/package.json b/package.json index 57af066d..19ccf0d9 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint": "tslint 'src/**/*.ts' 'addons/**/*.ts'", "test": "npm run test-unit", "posttest": "npm run lint", - "test-api": "mocha \"**/SerializeAddon.api.js\"", + "test-api": "mocha \"**/*.api.js\"", "test-unit": "node ./bin/test.js", "build": "tsc -b ./tsconfig.all.json", "prepare": "npm run build", diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 9f3aca87..540ef638 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IBufferCellColor as IBufferCellColorApi, IBufferCellFlags as IBufferCellFlagsApi } from 'xterm'; +import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi } from 'xterm'; import { ITerminal } from '../Types'; import { IBufferLine } from 'common/Types'; import { IBuffer } from 'common/buffer/Types'; @@ -14,7 +14,6 @@ import * as Strings from '../browser/LocalizableStrings'; import { IEvent } from 'common/EventEmitter'; import { AddonManager } from './AddonManager'; import { IParams } from 'common/parser/Types'; -import { AttributeData } from 'common/buffer/AttributeData'; export class Terminal implements ITerminalApi { private _core: ITerminal; @@ -225,65 +224,55 @@ class BufferLineApiView implements IBufferLineApi { } } -const fgFlagMask = FgFlags.BOLD | FgFlags.BLINK | FgFlags.INVERSE | FgFlags.INVISIBLE | FgFlags.UNDERLINE; -const bgFlagMask = BgFlags.DIM | BgFlags.ITALIC; -const colorMask = Attributes.CM_MASK | Attributes.RGB_MASK; +const FG_FLAG_MASK = FgFlags.BOLD | FgFlags.BLINK | FgFlags.INVERSE | FgFlags.INVISIBLE | FgFlags.UNDERLINE; +const BG_FLAG_MASK = BgFlags.DIM | BgFlags.ITALIC; +const COLOR_MASK = Attributes.CM_MASK | Attributes.RGB_MASK; class BufferCellApiView implements IBufferCellApi { - public flags: IBufferCellFlagsApi; - public fg: IBufferCellColorApi; - public bg: IBufferCellColorApi; - constructor(public cell: CellData) { - this.flags = { - get bold(): boolean { return !!(cell.fg & FgFlags.BOLD); }, - get underline(): boolean { return !!(cell.fg & FgFlags.UNDERLINE); }, - get blink(): boolean { return !!(cell.fg & FgFlags.BLINK); }, - get inverse(): boolean { return !!(cell.fg & FgFlags.INVERSE); }, - get invisible(): boolean { return !!(cell.fg & FgFlags.INVISIBLE); }, - get italic(): boolean { return !!(cell.bg & BgFlags.ITALIC); }, - get dim(): boolean { return !!(cell.bg & BgFlags.DIM); } - }; - this.fg = { - get colorMode(): 'RGB' | 'P256' | 'P16' | 'DEFAULT' { - switch (cell.getFgColorMode()) { - case Attributes.CM_RGB: return 'RGB'; - case Attributes.CM_P256: return 'P256'; - case Attributes.CM_P16: return 'P16'; - default: return 'DEFAULT'; - } - }, - get color(): number { return cell.getFgColor(); }, - get rgb(): [number, number, number] { return AttributeData.toColorRGB(cell.getFgColor()); } - }; - this.bg = { - get colorMode(): 'RGB' | 'P256' | 'P16' | 'DEFAULT' { - switch (cell.getBgColorMode()) { - case Attributes.CM_RGB: return 'RGB'; - case Attributes.CM_P256: return 'P256'; - case Attributes.CM_P16: return 'P16'; - default: return 'DEFAULT'; - } - }, - get color(): number { return cell.getBgColor(); }, - get rgb(): [number, number, number] { return AttributeData.toColorRGB(cell.getBgColor()); } - }; - } + constructor(public cell: CellData) {} + public get char(): string { return this.cell.getChars(); } public get width(): number { return this.cell.getWidth(); } - public isDefaultAttibutes(): boolean { - return this.cell.fg === 0 && this.cell.bg === 0; - } - public equalAttibutes(other: BufferCellApiView): boolean { - return this.cell.fg === other.cell.fg && this.cell.bg === other.cell.bg; - } + + public getWidth(): number { return this.cell.getWidth(); } + public getChars(): string { return this.cell.getChars(); } + public getCode(): number { return this.cell.getCode(); } + + public isInverse(): number { return this.cell.isInverse(); } + public isBold(): number { return this.cell.isBold(); } + public isUnderline(): number { return this.cell.isUnderline(); } + public isBlink(): number { return this.cell.isBlink(); } + public isInvisible(): number { return this.cell.isInvisible(); } + public isItalic(): number { return this.cell.isItalic(); } + public isDim(): number { return this.cell.isDim(); } + + public getFgColorMode(): number { return this.cell.getFgColorMode(); } + public getBgColorMode(): number { return this.cell.getBgColorMode(); } + public isFgRGB(): boolean { return this.cell.isFgRGB(); } + public isBgRGB(): boolean { return this.cell.isBgRGB(); } + public isFgPalette(): boolean { return this.cell.isFgPalette(); } + public isBgPalette(): boolean { return this.cell.isBgPalette(); } + public isFgPalette16(): boolean { return this.cell.getFgColorMode() == Attributes.CM_P16; } + public isBgPalette16(): boolean { return this.cell.getBgColorMode() == Attributes.CM_P16; } + public isFgPalette256(): boolean { return this.cell.getFgColorMode() == Attributes.CM_P256; } + public isBgPalette256(): boolean { return this.cell.getBgColorMode() == Attributes.CM_P256; } + + public isAttributeDefault(): boolean { return this.cell.fg === 0 && this.cell.bg === 0; } + public isFgDefault(): boolean { return this.cell.isFgDefault(); } + public isBgDefault(): boolean { return this.cell.isBgDefault(); } + + public getFgColor(): number { return this.cell.getFgColor(); } + public getBgColor(): number { return this.cell.getBgColor(); } + + public equalFlags(other: BufferCellApiView): boolean { - return (this.cell.fg & fgFlagMask) === (other.cell.fg & fgFlagMask) - && (this.cell.bg & bgFlagMask) === (other.cell.bg & bgFlagMask); + return (this.cell.fg & FG_FLAG_MASK) === (other.cell.fg & FG_FLAG_MASK) + && (this.cell.bg & BG_FLAG_MASK) === (other.cell.bg & BG_FLAG_MASK); } public equalFg(other: BufferCellApiView): boolean { - return (this.cell.fg & colorMask) === (other.cell.fg & colorMask); + return (this.cell.fg & COLOR_MASK) === (other.cell.fg & COLOR_MASK); } public equalBg(other: BufferCellApiView): boolean { - return (this.cell.bg & colorMask) === (other.cell.bg & colorMask); + return (this.cell.bg & COLOR_MASK) === (other.cell.bg & COLOR_MASK); } } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index b0415350..5043b867 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -942,49 +942,6 @@ declare module 'xterm' { translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string; } - /** - * Represents foreground and background color settings of a cell. - */ - interface IBufferCellColor { - /** - * Color mode of the color setting. - * RGB Color is an RGB color, use `.rgb` to grab the different channels. - * P256 Color is an indexed value of the 256 color palette. - * P16 Color is an indexed value of the 8 color palette (+8 for AIX bright colors). - * DEFAULT No color set, thus default color should be used. - */ - colorMode: 'RGB' | 'P256' | 'P16' | 'DEFAULT'; - - /** - * Color value set in the current color mode. - * Note that the color value can only be interpreted in conjunction - * with the color mode: - * RGB color contains 8 bit channels in RGB32 bitorder, e.g. red << 16 | green << 8 | blue - * P256 color contains indexed value 0..255 - * P16 color contains indexed value 0..15 - * DEFAULT color always contains -1 - */ - color: number; - - /** - * Helper to get RGB channels from color mode RGB. Reports channels as [red, green, blue]. - */ - rgb: [number, number, number]; - } - - /** - * Represents style flags of a cell. - */ - interface IBufferCellFlags { - readonly bold: boolean; - readonly underline: boolean; - readonly blink: boolean; - readonly inverse: boolean; - readonly invisible: boolean; - readonly italic: boolean; - readonly dim: boolean; - } - /** * Represents a single cell in the terminal's buffer. */ @@ -1003,48 +960,37 @@ declare module 'xterm' { */ readonly width: number; - /** - * Text attribute flags like bold, underline etc. - */ - readonly flags: IBufferCellFlags; + getWidth(): number; + getChars(): string; + getCode(): number; - /** - * Foreground color. - */ - readonly fg: IBufferCellColor; + getFgColorMode(): number; + getBgColorMode(): number; + getFgColor(): number; + getBgColor(): number; - /** - * Background color. - */ - readonly bg: IBufferCellColor; + isInverse(): number; + isBold(): number; + isUnderline(): number; + isBlink(): number; + isInvisible(): number; + isItalic(): number; + isDim(): number; - /** - * Whether cells have default attributes (flags and colors). - */ - isDefaultAttibutes(): boolean; + isFgRGB(): boolean; + isBgRGB(): boolean; + isFgPalette(): boolean; + isBgPalette(): boolean; + isFgPalette16(): boolean; + isBgPalette16(): boolean; + isFgPalette256(): boolean; + isBgPalette256(): boolean; + isAttributeDefault(): boolean; + isFgDefault(): boolean; + isBgDefault(): boolean; - /** - * Whether cells have the same text attributes (flags and colors). - * @param other Other cell. - */ - equalAttibutes(other: IBufferCell): boolean; - - /** - * Whether cells have the same text attribute flags. - * @param other Other cell. - */ - equalFlags(other: IBufferCell): boolean; - - /** - * Whether cells have the same foreground color. - * @param other Other cell. - */ - equalFg(other: IBufferCell): boolean; - - /** - * Whether cells have the same background color. - * @param other Other cell. - */ - equalBg(other: IBufferCell): boolean; + equalFg(cell: IBufferCell): boolean; + equalBg(cell: IBufferCell): boolean; + equalFlags(cell: IBufferCell): boolean; } }