Skip to content

Commit

Permalink
hooks for custom control sequences
Browse files Browse the repository at this point in the history
This fixes (at least partially) issue xtermjs#1176
"Add a way to plugin a custom control sequence handler".
  • Loading branch information
PerBothner committed Dec 5, 2018
1 parent b5f1c33 commit 4d660de
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 12 deletions.
74 changes: 66 additions & 8 deletions src/EscapeSequenceParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)];
}
Expand All @@ -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];
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
17 changes: 15 additions & 2 deletions src/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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).
Expand Down
6 changes: 5 additions & 1 deletion src/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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).
Expand Down
14 changes: 14 additions & 0 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/public/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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; }
Expand Down

0 comments on commit 4d660de

Please sign in to comment.