diff --git a/src/EscapeSequenceParser.ts b/src/EscapeSequenceParser.ts index b38c50f543..7a5da2c851 100644 --- a/src/EscapeSequenceParser.ts +++ b/src/EscapeSequenceParser.ts @@ -301,9 +301,39 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._executeHandlerFb = callback; } + private _removeHandler(array: any[], callback: any): void { + if (array) { + for (let i = array.length; --i >= 0; ) { + if (array[i] == callback) { + array.splice(i, 1); + return; + } + } + } + } + + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void { + let index = flag.charCodeAt(0); + let array = this._csiHandlers[index]; + if (! array) { this._csiHandlers[index] = array = new Array(); } + array.push(callback); + } + + removeCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void { + let index = flag.charCodeAt(0); + let array = this._csiHandlers[index]; + this._removeHandler(array, callback); + if (array && array.length == 0) + delete this._csiHandlers[index]; + } + /* deprecated */ setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void { - this._csiHandlers[flag.charCodeAt(0)] = callback; + this.clearCsiHandler(flag); + this.addCsiHandler(flag, (params: number[], collect: string): boolean => { + callback(params, collect); return true; + }); } + /* deprecated */ clearCsiHandler(flag: string): void { if (this._csiHandlers[flag.charCodeAt(0)]) delete this._csiHandlers[flag.charCodeAt(0)]; } @@ -321,9 +351,25 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._escHandlerFb = callback; } + addOscHandler(ident: number, callback: (data: string) => boolean): void { + let array = this._oscHandlers[ident]; + if (! array) { this._oscHandlers[ident] = array = new Array(); } + array.push(callback); + } + removeOscHandler(ident: number, callback: (data: string) => boolean): void { + let array = this._oscHandlers[ident]; + this._removeHandler(array, callback); + if (array && array.length == 0) + delete this._oscHandlers[ident]; + } + /* deprecated */ setOscHandler(ident: number, callback: (data: string) => void): void { - this._oscHandlers[ident] = callback; + this.clearOscHandler(ident); + this.addOscHandler(ident, (data: string): boolean => { + callback(data); return true; + }); } + /* deprecated */ clearOscHandler(ident: number): void { if (this._oscHandlers[ident]) delete this._oscHandlers[ident]; } @@ -463,9 +509,15 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } break; case ParserAction.CSI_DISPATCH: - callback = this._csiHandlers[code]; - if (callback) callback(params, collect); - else this._csiHandlerFb(collect, params, code); + let cHandler = this._csiHandlers[code]; + if (cHandler) { + for (let i = cHandler.length; ;) { + if (--i < 0) { cHandler = null; break; } + if ((cHandler[i])(params, collect)) + break; + } + } + if (! cHandler) this._csiHandlerFb(collect, params, code); break; case ParserAction.PARAM: if (code === 0x3b) params.push(0); @@ -532,9 +584,15 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // or with an explicit NaN OSC handler const identifier = parseInt(osc.substring(0, idx)); const content = osc.substring(idx + 1); - callback = this._oscHandlers[identifier]; - if (callback) callback(content); - else this._oscHandlerFb(identifier, content); + let oHandler = this._oscHandlers[identifier]; + if (oHandler) { + for (let i = oHandler.length; ;) { + if (--i < 0) { oHandler = null; break; } + if ((oHandler[i])(content)) + break; + } + } + if (! oHandler) this._oscHandlerFb(identifier, content); } } if (code === 0x1b) transition |= ParserState.ESCAPE; diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7604b01f69..2c931a2bb0 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, IInputHandlingTerminal } from './Types'; +import { IVtInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, IInputHandlingTerminal } from './Types'; import { C0, C1 } from './common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Buffer'; @@ -112,7 +112,7 @@ class DECRQSS implements IDcsHandler { * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand * each function's header comment. */ -export class InputHandler extends Disposable implements IInputHandler { +export class InputHandler extends Disposable implements IVtInputHandler { private _surrogateFirst: string; constructor( @@ -465,6 +465,19 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(buffer.y); } + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void { + this._parser.addCsiHandler(flag, callback); + } + removeCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void { + this._parser.removeCsiHandler(flag, callback); + } + addOscHandler(ident: number, callback: (data: string) => boolean): void { + this._parser.setOscHandler(ident, callback); + } + removeOscHandler(ident: number, callback: (data: string) => boolean): void { + this._parser.removeOscHandler(ident, callback); + } + /** * BEL * Bell (Ctrl-G). diff --git a/src/Terminal.ts b/src/Terminal.ts index 2cfc1ca88b..fea0cb699d 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler, IBufferLine } from './Types'; +import { IInputHandlingTerminal, IInputHandler, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler, IBufferLine } from './Types'; import { IMouseZoneManager } from './ui/Types'; import { IRenderer } from './renderer/Types'; import { BufferSet } from './BufferSet'; @@ -1286,6 +1286,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this.refresh(0, this.rows - 1); } + public get inputHandler(): IInputHandler { + return this._inputHandler; + } + /** * Scroll the display of the terminal by a number of pages. * @param pageCount The number of pages to scroll (negative scrolls up). diff --git a/src/Types.ts b/src/Types.ts index 430c6575bd..93dd7c8aa8 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -182,6 +182,16 @@ export interface IInputHandler { ESC ~ */ setgLevel(level: number): void; } +/* + * An InputHandler for VT-style terminals + */ +export interface IVtInputHandler extends IInputHandler { + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void; + removeCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void; + addOscHandler(ident: number, callback: (data: string) => boolean): void; + removeOscHandler(ident: number, callback: (data: string) => boolean): void; +} + export interface ILinkMatcher { id: number; regex: RegExp; @@ -492,6 +502,10 @@ export interface IEscapeSequenceParser extends IDisposable { setCsiHandler(flag: string, callback: (params: number[], 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): void; + removeCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): void; + addOscHandler(ident: number, callback: (data: string) => boolean): void; + removeOscHandler(ident: number, callback: (data: string) => boolean): void; setEscHandler(collectAndFlag: string, callback: () => void): void; clearEscHandler(collectAndFlag: string): void; diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 8ff7cf2b3b..de15fad08c 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -4,7 +4,7 @@ */ import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings } from 'xterm'; -import { ITerminal } from '../Types'; +import { ITerminal, IInputHandler } from '../Types'; import { Terminal as TerminalCore } from '../Terminal'; import * as Strings from '../Strings'; @@ -15,6 +15,9 @@ export class Terminal implements ITerminalApi { this._core = new TerminalCore(options); } + public get inputHandler(): IInputHandler { + return (this._core as TerminalCore).inputHandler; + } public get element(): HTMLElement { return this._core.element; } public get textarea(): HTMLTextAreaElement { return this._core.textarea; } public get rows(): number { return this._core.rows; }