From 8ceea112f7a4d11532b03d39a06d319c08c13f18 Mon Sep 17 00:00:00 2001 From: Per Bothner Date: Tue, 11 Dec 2018 17:18:10 -0800 Subject: [PATCH] hooks for custom control sequences This re-implements addCsiHandler/addOscHandler to return an IDisposable. --- src/EscapeSequenceParser.ts | 53 ++++++++++++++++++++++++++++++++++++- src/InputHandler.ts | 12 +++++++-- src/Terminal.ts | 6 ++++- src/Types.ts | 10 +++++++ src/public/Terminal.ts | 5 +++- 5 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/EscapeSequenceParser.ts b/src/EscapeSequenceParser.ts index f4898841..28562939 100644 --- a/src/EscapeSequenceParser.ts +++ b/src/EscapeSequenceParser.ts @@ -4,6 +4,7 @@ */ import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types'; +import { IDisposable } from 'xterm'; import { Disposable } from './common/Lifecycle'; /** @@ -41,7 +42,7 @@ export class TransitionTable { * @param action parser action to be done * @param next next parser state */ - add(code: number, state: number, action: number | null, next: number | null): void { + add(code: number, state: number, action: number | null, next: number | null): void { this.table[state << 8 | code] = ((action | 0) << 4) | ((next === undefined) ? state : next); } @@ -303,6 +304,32 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._executeHandlerFb = callback; } + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { + const index = flag.charCodeAt(0); + const oldHead = this._csiHandlers[index]; + const newHead = Object.assign( + (params: number[], collect: string): void => { + if (callback(params, collect)) { } + else if (newHead.nextHandler) { newHead.nextHandler(params, collect); } + else { this._csiHandlerFb(collect, params, index); } + }, + { nextHandler: oldHead, + dispose(): void { + let previous = null; let cur = this._csiHandlers[index]; + for (; cur && cur.nextHandler; + previous = cur, cur = cur.nextHandler) { + if (cur === newHead) { + if (previous) { previous.nextHandler = cur.nextHandler; } + else { this._csiHandlers[index] = cur.nextHandler; } + break; + } + } + } + }); + this._csiHandlers[index] = newHead; + return newHead; + } + setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void { this._csiHandlers[flag.charCodeAt(0)] = callback; } @@ -323,6 +350,30 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._escHandlerFb = callback; } + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable { + const oldHead = this._oscHandlers[ident]; + const newHead = Object.assign( + (data: string): void => { + if (callback(data)) { } + else if (newHead.nextHandler) { newHead.nextHandler(data); } + else { this._oscHandlerFb(ident, data); } + }, + { nextHandler: oldHead, + dispose(): void { + let previous = null; let cur = this._oscHandlers[ident]; + for (; cur && cur.nextHandler; + previous = cur, cur = cur.nextHandler) { + if (cur === newHead) { + if (previous) { previous.nextHandler = cur.nextHandler; } + else { this._oscHandlers[ident] = cur.nextHandler; } + break; + } + } + } + }); + this._oscHandlers[ident] = newHead; + return newHead; + } setOscHandler(ident: number, callback: (data: string) => void): void { this._oscHandlers[ident] = callback; } diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7604b01f..2e016296 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'; @@ -12,6 +12,7 @@ import { FLAGS } from './renderer/Types'; import { wcwidth } from './CharWidth'; import { EscapeSequenceParser } from './EscapeSequenceParser'; import { ICharset } from './core/Types'; +import { IDisposable } from 'xterm'; import { Disposable } from './common/Lifecycle'; /** @@ -112,7 +113,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 +466,13 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(buffer.y); } + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable { + return this._parser.addCsiHandler(flag, callback); + } + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable { + return this._parser.addOscHandler(ident, callback); + } + /** * BEL * Bell (Ctrl-G). diff --git a/src/Terminal.ts b/src/Terminal.ts index bc8fb103..8f66ebe9 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'; @@ -1287,6 +1287,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 a5aa8add..e150b90b 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -182,6 +182,14 @@ 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): IDisposable; + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; +} + export interface ILinkMatcher { id: number; regex: RegExp; @@ -492,6 +500,8 @@ 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): IDisposable; + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; setEscHandler(collectAndFlag: string, callback: () => void): void; clearEscHandler(collectAndFlag: string): void; diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 8ff7cf2b..de15fad0 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; }