diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 19d5f6a2c..73365af83 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -12,6 +12,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; import { Attributes } from 'common/buffer/Constants'; import { AttributeData } from 'common/buffer/AttributeData'; +import { Params } from 'common/parser/Params'; describe('InputHandler', () => { describe('save and restore cursor', () => { @@ -22,7 +23,7 @@ describe('InputHandler', () => { terminal.curAttrData.fg = 3; const inputHandler = new InputHandler(terminal); // Save cursor position - inputHandler.saveCursor([]); + inputHandler.saveCursor(); assert.equal(terminal.buffer.x, 1); assert.equal(terminal.buffer.y, 2); assert.equal(terminal.curAttrData.fg, 3); @@ -31,7 +32,7 @@ describe('InputHandler', () => { terminal.buffer.y = 20; terminal.curAttrData.fg = 30; // Restore cursor position - inputHandler.restoreCursor([]); + inputHandler.restoreCursor(); assert.equal(terminal.buffer.x, 1); assert.equal(terminal.buffer.y, 2); assert.equal(terminal.curAttrData.fg, 3); @@ -42,37 +43,37 @@ describe('InputHandler', () => { const inputHandler = new InputHandler(terminal); const collect = ' '; - inputHandler.setCursorStyle([0], collect); + inputHandler.setCursorStyle(Params.fromArray([0]), collect); assert.equal(terminal.options['cursorStyle'], 'block'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; - inputHandler.setCursorStyle([1], collect); + inputHandler.setCursorStyle(Params.fromArray([1]), collect); assert.equal(terminal.options['cursorStyle'], 'block'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; - inputHandler.setCursorStyle([2], collect); + inputHandler.setCursorStyle(Params.fromArray([2]), collect); assert.equal(terminal.options['cursorStyle'], 'block'); assert.equal(terminal.options['cursorBlink'], false); terminal.options = {}; - inputHandler.setCursorStyle([3], collect); + inputHandler.setCursorStyle(Params.fromArray([3]), collect); assert.equal(terminal.options['cursorStyle'], 'underline'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; - inputHandler.setCursorStyle([4], collect); + inputHandler.setCursorStyle(Params.fromArray([4]), collect); assert.equal(terminal.options['cursorStyle'], 'underline'); assert.equal(terminal.options['cursorBlink'], false); terminal.options = {}; - inputHandler.setCursorStyle([5], collect); + inputHandler.setCursorStyle(Params.fromArray([5]), collect); assert.equal(terminal.options['cursorStyle'], 'bar'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; - inputHandler.setCursorStyle([6], collect); + inputHandler.setCursorStyle(Params.fromArray([6]), collect); assert.equal(terminal.options['cursorStyle'], 'bar'); assert.equal(terminal.options['cursorBlink'], false); }); @@ -84,10 +85,10 @@ describe('InputHandler', () => { terminal.bracketedPasteMode = false; const inputHandler = new InputHandler(terminal); // Set bracketed paste mode - inputHandler.setMode([2004], collect); + inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); // Reset bracketed paste mode - inputHandler.resetMode([2004], collect); + inputHandler.resetMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, false); }); }); @@ -113,25 +114,25 @@ describe('InputHandler', () => { // insert one char from params = [0] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.insertChars([0]); + inputHandler.insertChars(Params.fromArray([0])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 123456789'); // insert one char from params = [1] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.insertChars([1]); + inputHandler.insertChars(Params.fromArray([1])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 12345678'); // insert two chars from params = [2] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.insertChars([2]); + inputHandler.insertChars(Params.fromArray([2])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 123456'); // insert 10 chars from params = [10] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.insertChars([10]); + inputHandler.insertChars(Params.fromArray([10])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a')); }); @@ -150,28 +151,28 @@ describe('InputHandler', () => { // delete one char from params = [0] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.deleteChars([0]); + inputHandler.deleteChars(Params.fromArray([0])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '234567890 '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '234567890'); // insert one char from params = [1] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.deleteChars([1]); + inputHandler.deleteChars(Params.fromArray([1])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '34567890 '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '34567890'); // insert two chars from params = [2] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.deleteChars([2]); + inputHandler.deleteChars(Params.fromArray([2])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '567890 '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '567890'); // insert 10 chars from params = [10] term.buffer.y = 0; term.buffer.x = 70; - inputHandler.deleteChars([10]); + inputHandler.deleteChars(Params.fromArray([10])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a')); }); @@ -187,19 +188,19 @@ describe('InputHandler', () => { // params[0] - right erase term.buffer.y = 0; term.buffer.x = 70; - inputHandler.eraseInLine([0]); + inputHandler.eraseInLine(Params.fromArray([0])); expect(term.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' '); // params[1] - left erase term.buffer.y = 1; term.buffer.x = 70; - inputHandler.eraseInLine([1]); + inputHandler.eraseInLine(Params.fromArray([1])); expect(term.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa'); // params[1] - left erase term.buffer.y = 2; term.buffer.x = 70; - inputHandler.eraseInLine([2]); + inputHandler.eraseInLine(Params.fromArray([2])); expect(term.buffer.lines.get(2).translateToString(false)).equals(Array(term.cols + 1).join(' ')); }); @@ -213,7 +214,7 @@ describe('InputHandler', () => { // params [0] - right and below erase term.buffer.y = 5; term.buffer.x = 40; - inputHandler.eraseInDisplay([0]); + inputHandler.eraseInDisplay(Params.fromArray([0])); expect(termContent(term, false)).eql([ Array(term.cols + 1).join('a'), Array(term.cols + 1).join('a'), @@ -241,7 +242,7 @@ describe('InputHandler', () => { // params [1] - left and above term.buffer.y = 5; term.buffer.x = 40; - inputHandler.eraseInDisplay([1]); + inputHandler.eraseInDisplay(Params.fromArray([1])); expect(termContent(term, false)).eql([ Array(term.cols + 1).join(' '), Array(term.cols + 1).join(' '), @@ -269,7 +270,7 @@ describe('InputHandler', () => { // params [2] - whole screen term.buffer.y = 5; term.buffer.x = 40; - inputHandler.eraseInDisplay([2]); + inputHandler.eraseInDisplay(Params.fromArray([2])); expect(termContent(term, false)).eql([ Array(term.cols + 1).join(' '), Array(term.cols + 1).join(' '), @@ -301,7 +302,7 @@ describe('InputHandler', () => { expect(term.buffer.lines.get(2).isWrapped).true; term.buffer.y = 2; term.buffer.x = 40; - inputHandler.eraseInDisplay([1]); + inputHandler.eraseInDisplay(Params.fromArray([1])); expect(term.buffer.lines.get(2).isWrapped).false; // reset and add a wrapped line @@ -316,7 +317,7 @@ describe('InputHandler', () => { expect(term.buffer.lines.get(2).isWrapped).true; term.buffer.y = 1; term.buffer.x = 90; // Cursor is beyond last column - inputHandler.eraseInDisplay([1]); + inputHandler.eraseInDisplay(Params.fromArray([1])); expect(term.buffer.lines.get(2).isWrapped).false; }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index a2ae4e089..b5c832938 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -15,7 +15,7 @@ import { concat } from 'common/TypedArrayUtils'; import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'common/input/TextDecoder'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types'; +import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams } from 'common/parser/Types'; import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; @@ -41,7 +41,7 @@ class DECRQSS implements IDcsHandler { constructor(private _terminal: any) { } - hook(collect: string, params: number[], flag: number): void { + hook(collect: string, params: IParams, flag: number): void { this._data = new Uint32Array(0); } @@ -131,8 +131,8 @@ export class InputHandler extends Disposable implements IInputHandler { /** * custom fallback handlers */ - this._parser.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { - this._terminal.error('Unknown CSI code: ', { collect, params, flag: String.fromCharCode(flag) }); + this._parser.setCsiHandlerFallback((collect: string, params: IParams, flag: number) => { + this._terminal.error('Unknown CSI code: ', { collect, params: params.toArray(), flag: String.fromCharCode(flag) }); }); this._parser.setEscHandlerFallback((collect: string, flag: number) => { this._terminal.error('Unknown ESC code: ', { collect, flag: String.fromCharCode(flag) }); @@ -252,8 +252,8 @@ export class InputHandler extends Disposable implements IInputHandler { /** * ESC handlers */ - this._parser.setEscHandler('7', () => this.saveCursor([])); - this._parser.setEscHandler('8', () => this.restoreCursor([])); + this._parser.setEscHandler('7', () => this.saveCursor()); + this._parser.setEscHandler('8', () => this.restoreCursor()); this._parser.setEscHandler('D', () => this.index()); this._parser.setEscHandler('E', () => this.nextLine()); this._parser.setEscHandler('H', () => this.tabSet()); @@ -476,7 +476,7 @@ export class InputHandler extends Disposable implements IInputHandler { /** * Forward addCsiHandler from parser. */ - public addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { + public addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable { return this._parser.addCsiHandler(flag, callback); } @@ -571,10 +571,10 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps @ * Insert Ps (Blank) Character(s) (default = 1) (ICH). */ - public insertChars(params: number[]): void { + public insertChars(params: IParams): void { this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells( this._terminal.buffer.x, - params[0] || 1, + params.params[0] || 1, this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) ); this._terminal.updateRange(this._terminal.buffer.y); @@ -584,11 +584,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps A * Cursor Up Ps Times (default = 1) (CUU). */ - public cursorUp(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorUp(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.y -= param; if (this._terminal.buffer.y < 0) { this._terminal.buffer.y = 0; @@ -599,11 +596,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps B * Cursor Down Ps Times (default = 1) (CUD). */ - public cursorDown(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorDown(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.y += param; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; @@ -618,11 +612,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps C * Cursor Forward Ps Times (default = 1) (CUF). */ - public cursorForward(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorForward(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.x += param; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; @@ -633,11 +624,9 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps D * Cursor Backward Ps Times (default = 1) (CUB). */ - public cursorBackward(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorBackward(params: IParams): void { + const param = params.params[0] || 1; + // If the end of the line is hit, prevent this action from wrapping around to the next line. if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x--; @@ -653,11 +642,8 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Next Line Ps Times (default = 1) (CNL). * same as CSI Ps B ? */ - public cursorNextLine(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorNextLine(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.y += param; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; @@ -671,11 +657,8 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Preceding Line Ps Times (default = 1) (CNL). * reuse CSI Ps A ? */ - public cursorPrecedingLine(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorPrecedingLine(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.y -= param; if (this._terminal.buffer.y < 0) { this._terminal.buffer.y = 0; @@ -688,11 +671,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps G * Cursor Character Absolute [column] (default = [row,1]) (CHA). */ - public cursorCharAbsolute(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public cursorCharAbsolute(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.x = param - 1; } @@ -700,15 +680,9 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps ; Ps H * Cursor Position [row;column] (default = [1,1]) (CUP). */ - public cursorPosition(params: number[]): void { - let col: number; - let row: number = params[0] - 1; - - if (params.length >= 2) { - col = params[1] - 1; - } else { - col = 0; - } + public cursorPosition(params: IParams): void { + let row: number = params.params[0] - 1; + let col: number = (params.length >= 2) ? params.params[1] - 1 : 0; if (row < 0) { row = 0; @@ -730,8 +704,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps I * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). */ - public cursorForwardTab(params: number[]): void { - let param = params[0] || 1; + public cursorForwardTab(params: IParams): void { + let param = params.params[0] || 1; while (param--) { this._terminal.buffer.x = this._terminal.buffer.nextStop(); } @@ -777,9 +751,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 1 -> Selective Erase Above. * Ps = 2 -> Selective Erase All. */ - public eraseInDisplay(params: number[]): void { + public eraseInDisplay(params: IParams): void { let j; - switch (params[0]) { + switch (params.params[0]) { case 0: j = this._terminal.buffer.y; this._terminal.updateRange(j); @@ -836,8 +810,8 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 1 -> Selective Erase to Left. * Ps = 2 -> Selective Erase All. */ - public eraseInLine(params: number[]): void { - switch (params[0]) { + public eraseInLine(params: IParams): void { + switch (params.params[0]) { case 0: this._eraseInBufferLine(this._terminal.buffer.y, this._terminal.buffer.x, this._terminal.cols); break; @@ -855,11 +829,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps L * Insert Ps Line(s) (default = 1) (IL). */ - public insertLines(params: number[]): void { - let param: number = params[0]; - if (param < 1) { - param = 1; - } + public insertLines(params: IParams): void { + let param = params.params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; @@ -884,11 +855,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps M * Delete Ps Line(s) (default = 1) (DL). */ - public deleteLines(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public deleteLines(params: IParams): void { + let param = params.params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; @@ -914,10 +882,10 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps P * Delete Ps Character(s) (default = 1) (DCH). */ - public deleteChars(params: number[]): void { + public deleteChars(params: IParams): void { this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells( this._terminal.buffer.x, - params[0] || 1, + params.params[0] || 1, this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) ); this._terminal.updateRange(this._terminal.buffer.y); @@ -926,8 +894,8 @@ export class InputHandler extends Disposable implements IInputHandler { /** * CSI Ps S Scroll up Ps lines (default = 1) (SU). */ - public scrollUp(params: number[]): void { - let param = params[0] || 1; + public scrollUp(params: IParams): void { + let param = params.params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; @@ -944,9 +912,9 @@ export class InputHandler extends Disposable implements IInputHandler { /** * CSI Ps T Scroll down Ps lines (default = 1) (SD). */ - public scrollDown(params: number[], collect?: string): void { + public scrollDown(params: IParams, collect?: string): void { if (params.length < 2 && !collect) { - let param = params[0] || 1; + let param = params.params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; @@ -965,10 +933,10 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps X * Erase Ps Character(s) (default = 1) (ECH). */ - public eraseChars(params: number[]): void { + public eraseChars(params: IParams): void { this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells( this._terminal.buffer.x, - this._terminal.buffer.x + (params[0] || 1), + this._terminal.buffer.x + (params.params[0] || 1), this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) ); this._terminal.updateRange(this._terminal.buffer.y); @@ -977,8 +945,8 @@ export class InputHandler extends Disposable implements IInputHandler { /** * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). */ - public cursorBackwardTab(params: number[]): void { - let param = params[0] || 1; + public cursorBackwardTab(params: IParams): void { + let param = params.params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; @@ -992,11 +960,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Pm ` Character Position Absolute * [column] (default = [row,1]) (HPA). */ - public charPosAbsolute(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public charPosAbsolute(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.x = param - 1; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; @@ -1008,11 +973,8 @@ export class InputHandler extends Disposable implements IInputHandler { * [columns] (default = [row,col+1]) (HPR) * reuse CSI Ps C ? */ - public hPositionRelative(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public hPositionRelative(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.x += param; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; @@ -1041,12 +1003,12 @@ export class InputHandler extends Disposable implements IInputHandler { * Note: To get reset on a valid sequence working correctly without much runtime penalty, * the preceding codepoint is stored on the parser in `this.print` and reset during `parser.parse`. */ - public repeatPrecedingCharacter(params: number[]): void { + public repeatPrecedingCharacter(params: IParams): void { if (!this._parser.precedingCodepoint) { return; } // call print to insert the chars and handle correct wrapping - const length = params[0] || 1; + const length = params.params[0] || 1; const data = new Uint32Array(length); for (let i = 0; i < length; ++i) { data[i] = this._parser.precedingCodepoint; @@ -1091,8 +1053,8 @@ export class InputHandler extends Disposable implements IInputHandler { * xterm/charproc.c - line 2012, for more information. * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) */ - public sendDeviceAttributes(params: number[], collect?: string): void { - if (params[0] > 0) { + public sendDeviceAttributes(params: IParams, collect?: string): void { + if (params.params[0] > 0) { return; } @@ -1113,7 +1075,7 @@ export class InputHandler extends Disposable implements IInputHandler { } else if (this._terminal.is('linux')) { // not supported by linux console. // linux console echoes parameters. - this._terminal.handler(params[0] + 'c'); + this._terminal.handler(params.params[0] + 'c'); } else if (this._terminal.is('screen')) { this._terminal.handler(C0.ESC + '[>83;40003;0c'); } @@ -1124,11 +1086,8 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Pm d Vertical Position Absolute (VPA) * [row] (default = [1,column]) */ - public linePosAbsolute(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public linePosAbsolute(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.y = param - 1; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; @@ -1140,11 +1099,8 @@ export class InputHandler extends Disposable implements IInputHandler { * [rows] (default = [row+1,column]) * reuse CSI Ps B ? */ - public vPositionRelative(params: number[]): void { - let param = params[0]; - if (param < 1) { - param = 1; - } + public vPositionRelative(params: IParams): void { + const param = params.params[0] || 1; this._terminal.buffer.y += param; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; @@ -1160,16 +1116,16 @@ export class InputHandler extends Disposable implements IInputHandler { * Horizontal and Vertical Position [row;column] (default = * [1,1]) (HVP). */ - public hVPosition(params: number[]): void { - if (params[0] < 1) params[0] = 1; - if (params[1] < 1) params[1] = 1; + public hVPosition(params: IParams): void { + const row = params.params[0] || 1; + const col = (params.length > 1) ? params.params[1] || 1 : 1; - this._terminal.buffer.y = params[0] - 1; + this._terminal.buffer.y = row - 1; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } - this._terminal.buffer.x = params[1] - 1; + this._terminal.buffer.x = col - 1; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; } @@ -1183,9 +1139,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 2 -> Clear Stops on Line. * http://vt100.net/annarbor/aaa-ug/section6.html */ - public tabClear(params: number[]): void { - const param = params[0]; - if (param <= 0) { + public tabClear(params: IParams): void { + const param = params.params[0]; + if (param === 0) { delete this._terminal.buffer.tabs[this._terminal.buffer.x]; } else if (param === 3) { this._terminal.buffer.tabs = {}; @@ -1278,15 +1234,13 @@ export class InputHandler extends Disposable implements IInputHandler { * Modes: * http: *vt100.net/docs/vt220-rm/chapter4.html */ - public setMode(params: number[], collect?: string): void { - if (params.length > 1) { - for (let i = 0; i < params.length; i++) { - this.setMode([params[i]]); - } - - return; + public setMode(params: IParams, collect?: string): void { + for (let i = 0; i < params.length; i++) { + this._setMode([params.params[i]], collect); } + } + private _setMode(params: number[], collect?: string): void { if (!collect) { switch (params[0]) { case 4: @@ -1380,10 +1334,10 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.cursorHidden = false; break; case 1048: // alt screen cursor - this.saveCursor(params); + this.saveCursor(); break; case 1049: // alt screen buffer cursor - this.saveCursor(params); + this.saveCursor(); // FALL-THROUGH case 47: // alt screen buffer case 1047: // alt screen buffer @@ -1483,15 +1437,13 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. * Ps = 2 0 0 4 -> Reset bracketed paste mode. */ - public resetMode(params: number[], collect?: string): void { - if (params.length > 1) { - for (let i = 0; i < params.length; i++) { - this.resetMode([params[i]]); - } - - return; + public resetMode(params: IParams, collect?: string): void { + for (let i = 0; i < params.length; i++) { + this._resetMode([params.params[i]], collect); } + } + private _resetMode(params: number[], collect?: string): void { if (!collect) { switch (params[0]) { case 4: @@ -1559,7 +1511,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.cursorHidden = true; break; case 1048: // alt screen cursor - this.restoreCursor(params); + this.restoreCursor(); break; case 1049: // alt screen buffer cursor // FALL-THROUGH @@ -1568,7 +1520,7 @@ export class InputHandler extends Disposable implements IInputHandler { // Ensure the selection manager has the correct buffer this._terminal.buffers.activateNormalBuffer(); if (params[0] === 1049) { - this.restoreCursor(params); + this.restoreCursor(); } this._terminal.refresh(0, this._terminal.rows - 1); if (this._terminal.viewport) { @@ -1648,9 +1600,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 4 8 ; 5 ; Ps -> Set background color to the second * Ps. */ - public charAttributes(params: number[]): void { + public charAttributes(params: IParams): void { // Optimize a single SGR0. - if (params.length === 1 && params[0] === 0) { + if (params.length === 1 && params.params[0] === 0) { this._terminal.curAttrData.fg = DEFAULT_ATTR_DATA.fg; this._terminal.curAttrData.bg = DEFAULT_ATTR_DATA.bg; return; @@ -1661,7 +1613,7 @@ export class InputHandler extends Disposable implements IInputHandler { const attr = this._terminal.curAttrData; for (let i = 0; i < l; i++) { - p = params[i]; + p = params.params[i]; if (p >= 30 && p <= 37) { // fg color 8 attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK); @@ -1733,29 +1685,30 @@ export class InputHandler extends Disposable implements IInputHandler { attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK); } else if (p === 38) { // fg color 256 and RGB - if (params[i + 1] === 2) { + // FIXME: apply sub parameter, avoid reading over length! + if (params.params[i + 1] === 2) { i += 2; attr.fg |= Attributes.CM_RGB; attr.fg &= ~Attributes.RGB_MASK; - attr.fg |= AttributeData.fromColorRGB([params[i], params[i + 1], params[i + 2]]); + attr.fg |= AttributeData.fromColorRGB([params.params[i], params.params[i + 1], params.params[i + 2]]); i += 2; - } else if (params[i + 1] === 5) { + } else if (params.params[i + 1] === 5) { i += 2; - p = params[i] & 0xff; + p = params.params[i] & 0xff; attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK); attr.fg |= Attributes.CM_P256 | p; } } else if (p === 48) { // bg color 256 and RGB - if (params[i + 1] === 2) { + if (params.params[i + 1] === 2) { i += 2; attr.bg |= Attributes.CM_RGB; attr.bg &= ~Attributes.RGB_MASK; - attr.bg |= AttributeData.fromColorRGB([params[i], params[i + 1], params[i + 2]]); + attr.bg |= AttributeData.fromColorRGB([params.params[i], params.params[i + 1], params.params[i + 2]]); i += 2; - } else if (params[i + 1] === 5) { + } else if (params.params[i + 1] === 5) { i += 2; - p = params[i] & 0xff; + p = params.params[i] & 0xff; attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK); attr.bg |= Attributes.CM_P256 | p; } @@ -1794,9 +1747,9 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI ? 5 3 n Locator available, if compiled-in, or * CSI ? 5 0 n No Locator, if not. */ - public deviceStatus(params: number[], collect?: string): void { + public deviceStatus(params: IParams, collect?: string): void { if (!collect) { - switch (params[0]) { + switch (params.params[0]) { case 5: // status report this._onData.fire(`${C0.ESC}[0n`); @@ -1811,7 +1764,7 @@ export class InputHandler extends Disposable implements IInputHandler { } else if (collect === '?') { // modern xterm doesnt seem to // respond to any of these except ?6, 6, and 5 - switch (params[0]) { + switch (params.params[0]) { case 6: // cursor position const y = this._terminal.buffer.y + 1; @@ -1842,7 +1795,7 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI ! p Soft terminal reset (DECSTR). * http://vt100.net/docs/vt220-rm/table4-10.html */ - public softReset(params: number[], collect?: string): void { + public softReset(params: IParams, collect?: string): void { if (collect === '!') { this._terminal.cursorHidden = false; this._terminal.insertMode = false; @@ -1873,9 +1826,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 5 -> blinking bar (xterm). * Ps = 6 -> steady bar (xterm). */ - public setCursorStyle(params?: number[], collect?: string): void { + public setCursorStyle(params?: IParams, collect?: string): void { if (collect === ' ') { - const param = params[0] < 1 ? 1 : params[0]; + const param = params.params[0] || 1; switch (param) { case 1: case 2: @@ -1901,12 +1854,12 @@ export class InputHandler extends Disposable implements IInputHandler { * dow) (DECSTBM). * CSI ? Pm r */ - public setScrollRegion(params: number[], collect?: string): void { + public setScrollRegion(params: IParams, collect?: string): void { if (collect) { return; } - this._terminal.buffer.scrollTop = (params[0] || 1) - 1; - this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1; + this._terminal.buffer.scrollTop = (params.params[0] || 1) - 1; + this._terminal.buffer.scrollBottom = (params.length > 1 && params.params[1] && params.params[1] <= this._terminal.rows ? params.params[1] : this._terminal.rows) - 1; this._terminal.buffer.x = 0; this._terminal.buffer.y = 0; } @@ -1917,7 +1870,7 @@ export class InputHandler extends Disposable implements IInputHandler { * ESC 7 * Save cursor (ANSI.SYS). */ - public saveCursor(params: number[]): void { + public saveCursor(params?: IParams): void { this._terminal.buffer.savedX = this._terminal.buffer.x; this._terminal.buffer.savedY = this._terminal.buffer.ybase + this._terminal.buffer.y; this._terminal.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg; @@ -1930,7 +1883,7 @@ export class InputHandler extends Disposable implements IInputHandler { * ESC 8 * Restore cursor (ANSI.SYS). */ - public restoreCursor(params: number[]): void { + public restoreCursor(params?: IParams): void { this._terminal.buffer.x = this._terminal.buffer.savedX || 0; this._terminal.buffer.y = Math.max(this._terminal.buffer.savedY - this._terminal.buffer.ybase, 0); this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg; diff --git a/src/Terminal.ts b/src/Terminal.ts index b28cfcfa0..29a245001 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -56,6 +56,7 @@ import { Disposable } from 'common/Lifecycle'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { Attributes } from 'common/buffer/Constants'; import { MouseService } from 'browser/services/MouseService'; +import { IParams } from 'common/parser/Types'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -1413,7 +1414,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } /** Add handler for CSI escape sequence. See xterm.d.ts for details. */ - public addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { + public addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable { return this._inputHandler.addCsiHandler(flag, callback); } /** Add handler for OSC escape sequence. See xterm.d.ts for details. */ diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 07f917055..52887f197 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -15,6 +15,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { IColorManager, IColorSet } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { EventEmitter } from 'common/EventEmitter'; +import { IParams } from 'common/parser/Types'; export class TestTerminal extends Terminal { writeSync(data: string): void { @@ -70,7 +71,7 @@ export class MockTerminal implements ITerminal { attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { throw new Error('Method not implemented.'); } - addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { + addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable { throw new Error('Method not implemented.'); } addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable { diff --git a/src/Types.d.ts b/src/Types.d.ts index 5987d605e..d5399b807 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -9,6 +9,7 @@ import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IColorSet } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; +import { IParams } from 'common/parser/Types'; export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean; @@ -112,42 +113,42 @@ export interface IInputHandler { /** C0 SO */ shiftOut(): void; /** C0 SI */ shiftIn(): void; - /** CSI @ */ insertChars(params?: number[]): void; - /** CSI A */ cursorUp(params?: number[]): void; - /** CSI B */ cursorDown(params?: number[]): void; - /** CSI C */ cursorForward(params?: number[]): void; - /** CSI D */ cursorBackward(params?: number[]): void; - /** CSI E */ cursorNextLine(params?: number[]): void; - /** CSI F */ cursorPrecedingLine(params?: number[]): void; - /** CSI G */ cursorCharAbsolute(params?: number[]): void; - /** CSI H */ cursorPosition(params?: number[]): void; - /** CSI I */ cursorForwardTab(params?: number[]): void; - /** CSI J */ eraseInDisplay(params?: number[]): void; - /** CSI K */ eraseInLine(params?: number[]): void; - /** CSI L */ insertLines(params?: number[]): void; - /** CSI M */ deleteLines(params?: number[]): void; - /** CSI P */ deleteChars(params?: number[]): void; - /** CSI S */ scrollUp(params?: number[]): void; - /** CSI T */ scrollDown(params?: number[], collect?: string): void; - /** CSI X */ eraseChars(params?: number[]): void; - /** CSI Z */ cursorBackwardTab(params?: number[]): void; - /** CSI ` */ charPosAbsolute(params?: number[]): void; - /** CSI a */ hPositionRelative(params?: number[]): void; - /** CSI b */ repeatPrecedingCharacter(params?: number[]): void; - /** CSI c */ sendDeviceAttributes(params?: number[], collect?: string): void; - /** CSI d */ linePosAbsolute(params?: number[]): void; - /** CSI e */ vPositionRelative(params?: number[]): void; - /** CSI f */ hVPosition(params?: number[]): void; - /** CSI g */ tabClear(params?: number[]): void; - /** CSI h */ setMode(params?: number[], collect?: string): void; - /** CSI l */ resetMode(params?: number[], collect?: string): void; - /** CSI m */ charAttributes(params?: number[]): void; - /** CSI n */ deviceStatus(params?: number[], collect?: string): void; - /** CSI p */ softReset(params?: number[], collect?: string): void; - /** CSI q */ setCursorStyle(params?: number[], collect?: string): void; - /** CSI r */ setScrollRegion(params?: number[], collect?: string): void; - /** CSI s */ saveCursor(params?: number[]): void; - /** CSI u */ restoreCursor(params?: number[]): void; + /** CSI @ */ insertChars(params?: IParams): void; + /** CSI A */ cursorUp(params?: IParams): void; + /** CSI B */ cursorDown(params?: IParams): void; + /** CSI C */ cursorForward(params?: IParams): void; + /** CSI D */ cursorBackward(params?: IParams): void; + /** CSI E */ cursorNextLine(params?: IParams): void; + /** CSI F */ cursorPrecedingLine(params?: IParams): void; + /** CSI G */ cursorCharAbsolute(params?: IParams): void; + /** CSI H */ cursorPosition(params?: IParams): void; + /** CSI I */ cursorForwardTab(params?: IParams): void; + /** CSI J */ eraseInDisplay(params?: IParams): void; + /** CSI K */ eraseInLine(params?: IParams): void; + /** CSI L */ insertLines(params?: IParams): void; + /** CSI M */ deleteLines(params?: IParams): void; + /** CSI P */ deleteChars(params?: IParams): void; + /** CSI S */ scrollUp(params?: IParams): void; + /** CSI T */ scrollDown(params?: IParams, collect?: string): void; + /** CSI X */ eraseChars(params?: IParams): void; + /** CSI Z */ cursorBackwardTab(params?: IParams): void; + /** CSI ` */ charPosAbsolute(params?: IParams): void; + /** CSI a */ hPositionRelative(params?: IParams): void; + /** CSI b */ repeatPrecedingCharacter(params?: IParams): void; + /** CSI c */ sendDeviceAttributes(params?: IParams, collect?: string): void; + /** CSI d */ linePosAbsolute(params?: IParams): void; + /** CSI e */ vPositionRelative(params?: IParams): void; + /** CSI f */ hVPosition(params?: IParams): void; + /** CSI g */ tabClear(params?: IParams): void; + /** CSI h */ setMode(params?: IParams, collect?: string): void; + /** CSI l */ resetMode(params?: IParams, collect?: string): void; + /** CSI m */ charAttributes(params?: IParams): void; + /** CSI n */ deviceStatus(params?: IParams, collect?: string): void; + /** CSI p */ softReset(params?: IParams, collect?: string): void; + /** CSI q */ setCursorStyle(params?: IParams, collect?: string): void; + /** CSI r */ setScrollRegion(params?: IParams, collect?: string): void; + /** CSI s */ saveCursor(params?: IParams): void; + /** CSI u */ restoreCursor(params?: IParams): void; /** OSC 0 OSC 2 */ setTitle(data: string): void; /** ESC E */ nextLine(): void; @@ -245,7 +246,7 @@ export interface IPublicTerminal extends IDisposable { writeln(data: string): void; open(parent: HTMLElement): void; attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; - addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; + addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable; addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number; deregisterLinkMatcher(matcherId: number): void; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index f6e46a765..0777c9215 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -69,14 +69,14 @@ const testTerminal: any = { actionExecute: function (flag: string): void { this.calls.push(['exe', flag]); }, - actionCSI: function (collect: string, params: number[], flag: string): void { - this.calls.push(['csi', collect, params, flag]); + actionCSI: function (collect: string, params: IParams, flag: string): void { + this.calls.push(['csi', collect, params.toArray() as number[], flag]); }, actionESC: function (collect: string, flag: string): void { this.calls.push(['esc', collect, flag]); }, - actionDCSHook: function (collect: string, params: number[], flag: string): void { - this.calls.push(['dcs hook', collect, params, flag]); + actionDCSHook: function (collect: string, params: IParams, flag: string): void { + this.calls.push(['dcs hook', collect, params.toArray() as number[], flag]); }, actionDCSPrint: function (data: Uint32Array, start: number, end: number): void { let s = ''; @@ -92,7 +92,7 @@ const testTerminal: any = { // dcs handler to map dcs actions into the test object `testTerminal` class DcsTest implements IDcsHandler { - hook(collect: string, params: number[], flag: number): void { + hook(collect: string, params: IParams, flag: number): void { testTerminal.actionDCSHook(collect, params, String.fromCharCode(flag)); } put(data: Uint32Array, start: number, end: number): void { @@ -124,7 +124,7 @@ let state: any; // parser with Uint8Array based transition table const testParser = new TestEscapeSequenceParser(); testParser.setPrintHandler(testTerminal.print.bind(testTerminal)); -testParser.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { +testParser.setCsiHandlerFallback((collect: string, params: IParams, flag: number) => { testTerminal.actionCSI(collect, params, String.fromCharCode(flag)); }); testParser.setEscHandlerFallback((collect: string, flag: number) => { @@ -1146,8 +1146,8 @@ describe('EscapeSequenceParser', function (): void { chai.expect(esc).eql([]); }); it('CSI handler', function (): void { - parser2.setCsiHandler('m', function (params: number[], collect: string): void { - csi.push(['m', params, collect]); + parser2.setCsiHandler('m', function (params: IParams, collect: string): void { + csi.push(['m', params.toArray() as number[], collect]); }); parse(parser2, INPUT); chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]); @@ -1160,16 +1160,16 @@ describe('EscapeSequenceParser', function (): void { describe('CSI custom handlers', () => { it('Prevent fallback', () => { const csiCustom: [string, number[], string][] = []; - parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect])); - parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; }); + parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params.toArray() as number[], collect])); + parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params.toArray() as number[], collect]); return true; }); parse(parser2, INPUT); chai.expect(csi).eql([], 'Should not fallback to original handler'); chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]); }); it('Allow fallback', () => { const csiCustom: [string, number[], string][] = []; - parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect])); - parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return false; }); + parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params.toArray() as number[], collect])); + parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params.toArray() as number[], collect]); return false; }); parse(parser2, INPUT); chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler'); chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]); @@ -1177,9 +1177,9 @@ describe('EscapeSequenceParser', function (): void { it('Multiple custom handlers fallback once', () => { const csiCustom: [string, number[], string][] = []; const csiCustom2: [string, number[], string][] = []; - parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect])); - parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; }); - parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params, collect]); return false; }); + parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params.toArray() as number[], collect])); + parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params.toArray() as number[], collect]); return true; }); + parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params.toArray() as number[], collect]); return false; }); parse(parser2, INPUT); chai.expect(csi).eql([], 'Should not fallback to original handler'); chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]); @@ -1188,9 +1188,9 @@ describe('EscapeSequenceParser', function (): void { it('Multiple custom handlers no fallback', () => { const csiCustom: [string, number[], string][] = []; const csiCustom2: [string, number[], string][] = []; - parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect])); - parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; }); - parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params, collect]); return true; }); + parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params.toArray() as number[], collect])); + parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params.toArray() as number[], collect]); return true; }); + parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params.toArray() as number[], collect]); return true; }); parse(parser2, INPUT); chai.expect(csi).eql([], 'Should not fallback to original handler'); chai.expect(csiCustom).eql([], 'Should not fallback once'); @@ -1206,8 +1206,8 @@ describe('EscapeSequenceParser', function (): void { }); it('Dispose should work', () => { const csiCustom: [string, number[], string][] = []; - parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect])); - const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; }); + parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params.toArray() as number[], collect])); + const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params.toArray() as number[], collect]); return true; }); customHandler.dispose(); parse(parser2, INPUT); chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]); @@ -1215,8 +1215,8 @@ describe('EscapeSequenceParser', function (): void { }); it('Should not corrupt the parser when dispose is called twice', () => { const csiCustom: [string, number[], string][] = []; - parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect])); - const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; }); + parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params.toArray() as number[], collect])); + const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params.toArray() as number[], collect]); return true; }); customHandler.dispose(); customHandler.dispose(); parse(parser2, INPUT); @@ -1320,8 +1320,8 @@ describe('EscapeSequenceParser', function (): void { }); it('DCS handler', function (): void { parser2.setDcsHandler('+p', { - hook: function (collect: string, params: number[], flag: number): void { - dcs.push(['hook', collect, params, flag]); + hook: function (collect: string, params: IParams, flag: number): void { + dcs.push(['hook', collect, params.toArray() as number[], flag]); }, put: function (data: Uint32Array, start: number, end: number): void { let s = ''; @@ -1361,7 +1361,7 @@ describe('EscapeSequenceParser', function (): void { currentState: ParserState.CSI_PARAM, osc: '', collect: '', - params: [1, 2, 0], // extra zero here + params: Params.fromArray([1, 2, 0]), // extra zero here abort: false }); parser2.clearErrorHandler(); diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 843eb4c91..81b70d235 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types'; +import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams } from 'common/parser/Types'; import { ParserState, ParserAction } from 'common/parser/Constants'; import { Disposable } from 'common/Lifecycle'; import { utf32ToString } from 'common/input/TextDecoder'; @@ -15,7 +15,7 @@ interface IHandlerCollection { [key: string]: T[]; } -type CsiHandler = (params: number[], collect: string) => boolean | void; +type CsiHandler = (params: IParams, collect: string) => boolean | void; type OscHandler = (data: string) => boolean | void; /** @@ -207,7 +207,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { * Dummy DCS handler as default fallback. */ class DcsDummy implements IDcsHandler { - hook(collect: string, params: number[], flag: number): void { } + hook(collect: string, params: IParams, flag: number): void { } put(data: Uint32Array, start: number, end: number): void { } unhook(): void { } } @@ -247,7 +247,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // fallback handlers protected _printHandlerFb: (data: Uint32Array, start: number, end: number) => void; protected _executeHandlerFb: (code: number) => void; - protected _csiHandlerFb: (collect: string, params: number[], flag: number) => void; + protected _csiHandlerFb: (collect: string, params: IParams, flag: number) => void; protected _escHandlerFb: (collect: string, flag: number) => void; protected _oscHandlerFb: (identifier: number, data: string) => void; protected _dcsHandlerFb: IDcsHandler; @@ -267,7 +267,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // set default fallback handlers and handler lookup containers this._printHandlerFb = (data, start, end): void => { }; this._executeHandlerFb = (code: number): void => { }; - this._csiHandlerFb = (collect: string, params: number[], flag: number): void => { }; + this._csiHandlerFb = (collect: string, params: IParams, flag: number): void => { }; this._escHandlerFb = (collect: string, flag: number): void => { }; this._oscHandlerFb = (identifier: number, data: string): void => { }; this._dcsHandlerFb = new DcsDummy(); @@ -325,13 +325,13 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } }; } - setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void { + setCsiHandler(flag: string, callback: (params: IParams, collect: string) => void): void { this._csiHandlers[flag.charCodeAt(0)] = [callback]; } clearCsiHandler(flag: string): void { if (this._csiHandlers[flag.charCodeAt(0)]) delete this._csiHandlers[flag.charCodeAt(0)]; } - setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void { + setCsiHandlerFallback(callback: (collect: string, params: IParams, flag: number) => void): void { this._csiHandlerFb = callback; } @@ -469,7 +469,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP currentState, osc, collect, - params: params.toArray() as number[], + params: params, abort: false }); if (inject.abort) return; @@ -485,12 +485,12 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP let j = handlers ? handlers.length - 1 : -1; for (; j >= 0; j--) { // undefined or true means success and to stop bubbling - if (handlers[j](params.toArray() as number[], collect) !== false) { + if (handlers[j](params, collect) !== false) { break; } } if (j < 0) { - this._csiHandlerFb(collect, params.toArray() as number[], code); + this._csiHandlerFb(collect, params, code); } break; case ParserAction.PARAM: @@ -532,7 +532,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this.precedingCodepoint = 0; dcsHandler = this._dcsHandlers[collect + String.fromCharCode(code)]; if (!dcsHandler) dcsHandler = this._dcsHandlerFb; - dcsHandler.hook(collect, params.toArray() as number[], code); + dcsHandler.hook(collect, params, code); break; case ParserAction.DCS_PUT: // inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f diff --git a/src/common/parser/Types.d.ts b/src/common/parser/Types.d.ts index ffd175d66..17f573776 100644 --- a/src/common/parser/Types.d.ts +++ b/src/common/parser/Types.d.ts @@ -60,7 +60,7 @@ export interface IParsingState { // collect buffer with intermediate characters collect: string; // params buffer - params: number[]; + params: IParams; // should abort (default: false) abort: boolean; } @@ -87,7 +87,7 @@ export interface IParsingState { * `unhook` marks the end of the current DCS sequence. */ export interface IDcsHandler { - hook(collect: string, params: number[], flag: number): void; + hook(collect: string, params: IParams, flag: number): void; put(data: Uint32Array, start: number, end: number): void; unhook(): void; } @@ -120,10 +120,10 @@ export interface IEscapeSequenceParser extends IDisposable { clearExecuteHandler(flag: string): void; setExecuteHandlerFallback(callback: (code: number) => void): void; - setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void; + setCsiHandler(flag: string, callback: (params: IParams, collect: string) => void): void; clearCsiHandler(flag: string): void; - setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void; - addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; + setCsiHandlerFallback(callback: (collect: string, params: IParams, flag: number) => void): void; + addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable; addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; setEscHandler(collectAndFlag: string, callback: () => void): void; diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 0dae4def8..704862073 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 } from 'xterm'; +import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParams } from 'xterm'; import { ITerminal } from '../Types'; import { IBufferLine } from 'common/Types'; import { IBuffer } from 'common/buffer/Types'; @@ -55,7 +55,7 @@ export class Terminal implements ITerminalApi { public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { this._core.attachCustomKeyEventHandler(customKeyEventHandler); } - public addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { + public addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable { return this._core.addCsiHandler(flag, callback); } public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable { diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index f435367dd..d2c16e1d0 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -493,7 +493,7 @@ declare module 'xterm' { * The most recently-added handler is tried first. * @return An IDisposable you can call to remove this handler. */ - addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; + addCsiHandler(flag: string, callback: (params: IParams, collect: string) => boolean): IDisposable; /** * (EXPERIMENTAL) Adds a handler for OSC escape sequences. @@ -925,4 +925,28 @@ declare module 'xterm' { */ readonly width: number; } + + interface IParams { + /** from ctor */ + maxLength: number; + maxSubParamsLength: number; + + /** param values and its length */ + params: Int16Array; + length: number; + + /** sub params and its length */ + subParams: Int16Array; + subParamsLength: number; + + /** methods */ + clone(): IParams; + toArray(): (number | number[])[]; + reset(): void; + addParam(value: number): void; + addSubParam(value: number): void; + hasSubParams(idx: number): boolean; + getSubParams(idx: number): Int16Array | null; + getSubParamsAll(): {[idx: number]: Int16Array}; + } }