cleanup interfaces

This commit is contained in:
Jörg Breitbart
2019-08-11 17:55:24 +02:00
parent f677516e95
commit b35383915d
3 changed files with 160 additions and 133 deletions
+14 -14
View File
@@ -1220,26 +1220,26 @@ describe('EscapeSequenceParser', function (): void {
});
describe('ESC custom handlers', () => {
it('prevent fallback', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom - %G']);
});
it('allow fallback', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return false; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom - %G', 'default - %G']);
});
it('Multiple custom handlers fallback once', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return false; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom2 - %G', 'custom - %G']);
});
it('Multiple custom handlers no fallback', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return true; });
parse(parser2, INPUT);
@@ -1247,21 +1247,21 @@ describe('EscapeSequenceParser', function (): void {
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => order.push(1));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { order.push(1); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { order.push(2); return false; });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { order.push(3); return false; });
parse(parser2, '\x1b%G');
chai.expect(order).eql([3, 2, 1]);
});
it('Dispose should work', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
const dispo = parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
dispo.dispose();
parse(parser2, INPUT);
chai.expect(esc).eql(['default - %G']);
});
it('Should not corrupt the parser when dispose is called twice', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
const dispo = parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
dispo.dispose();
dispo.dispose();
@@ -1284,7 +1284,7 @@ describe('EscapeSequenceParser', function (): void {
describe('CSI custom handlers', () => {
it('Prevent fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parse(parser2, INPUT);
chai.expect(csi).eql([], 'Should not fallback to original handler');
@@ -1292,7 +1292,7 @@ describe('EscapeSequenceParser', function (): void {
});
it('Allow fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return false; });
parse(parser2, INPUT);
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler');
@@ -1301,7 +1301,7 @@ describe('EscapeSequenceParser', function (): void {
it('Multiple custom handlers fallback once', () => {
const csiCustom: [string, ParamsArray, string][] = [];
const csiCustom2: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return false; });
parse(parser2, INPUT);
@@ -1312,7 +1312,7 @@ describe('EscapeSequenceParser', function (): void {
it('Multiple custom handlers no fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
const csiCustom2: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return true; });
parse(parser2, INPUT);
@@ -1322,7 +1322,7 @@ describe('EscapeSequenceParser', function (): void {
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.setCsiHandler({final: 'm'}, () => order.push(1));
parser2.setCsiHandler({final: 'm'}, () => { order.push(1); });
parser2.addCsiHandler({final: 'm'}, () => { order.push(2); return false; });
parser2.addCsiHandler({final: 'm'}, () => { order.push(3); return false; });
parse(parser2, '\x1b[0m');
@@ -1330,7 +1330,7 @@ describe('EscapeSequenceParser', function (): void {
});
it('Dispose should work', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
const customHandler = parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
customHandler.dispose();
parse(parser2, INPUT);
@@ -1339,7 +1339,7 @@ describe('EscapeSequenceParser', function (): void {
});
it('Should not corrupt the parser when dispose is called twice', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
const customHandler = parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
customHandler.dispose();
customHandler.dispose();
+66 -66
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandler, OscFallbackHandler, IOscParser, EscHandler, IDcsParser, DcsFallbackHandler, IFunctionIdentifier } from 'common/parser/Types';
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandler, OscFallbackHandler, IOscParser, EscHandler, IDcsParser, DcsFallbackHandler, IFunctionIdentifier, ExecuteFallbackHandler, CsiFallbackHandler, EscFallbackHandler, PrintHandler, PrintFallbackHandler, ExecuteHandler } from 'common/parser/Types';
import { ParserState, ParserAction } from 'common/parser/Constants';
import { Disposable } from 'common/Lifecycle';
import { IDisposable } from 'common/Types';
@@ -238,7 +238,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
protected _collect: number;
// handler lookup containers
protected _printHandler: (data: Uint32Array, start: number, end: number) => void;
protected _printHandler: PrintHandler;
protected _executeHandlers: any;
protected _csiHandlers: IHandlerCollection<CsiHandler>;
protected _escHandlers: IHandlerCollection<EscHandler>;
@@ -247,10 +247,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
protected _errorHandler: (state: IParsingState) => IParsingState;
// fallback handlers
protected _printHandlerFb: (data: Uint32Array, start: number, end: number) => void;
protected _executeHandlerFb: (code: number) => void;
protected _csiHandlerFb: (ident: number, params: IParams) => void;
protected _escHandlerFb: (ident: number) => void;
protected _printHandlerFb: PrintFallbackHandler;
protected _executeHandlerFb: ExecuteFallbackHandler;
protected _csiHandlerFb: CsiFallbackHandler;
protected _escHandlerFb: EscFallbackHandler;
protected _errorHandlerFb: (state: IParsingState) => IParsingState;
constructor(readonly TRANSITIONS: TransitionTable = VT500_TRANSITION_TABLE) {
@@ -334,41 +334,67 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._dcsParser.dispose();
}
public setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void {
this._printHandler = callback;
public setPrintHandler(handler: PrintHandler): void {
this._printHandler = handler;
}
public clearPrintHandler(): void {
this._printHandler = this._printHandlerFb;
}
public setExecuteHandler(flag: string, callback: () => void): void {
this._executeHandlers[flag.charCodeAt(0)] = callback;
}
public clearExecuteHandler(flag: string): void {
if (this._executeHandlers[flag.charCodeAt(0)]) delete this._executeHandlers[flag.charCodeAt(0)];
}
public setExecuteHandlerFallback(callback: (code: number) => void): void {
this._executeHandlerFb = callback;
}
public addCsiHandler(id: IFunctionIdentifier, callback: CsiHandler): IDisposable {
const ident = this._identifier(id);
if (this._csiHandlers[ident] === undefined) {
this._csiHandlers[ident] = [];
public addEscHandler(id: IFunctionIdentifier, handler: EscHandler): IDisposable {
const ident = this._identifier(id, [0x30, 0x7e]);
if (this._escHandlers[ident] === undefined) {
this._escHandlers[ident] = [];
}
const handlerList = this._csiHandlers[ident];
handlerList.push(callback);
const handlerList = this._escHandlers[ident];
handlerList.push(handler);
return {
dispose: () => {
const handlerIndex = handlerList.indexOf(callback);
const handlerIndex = handlerList.indexOf(handler);
if (handlerIndex !== -1) {
handlerList.splice(handlerIndex, 1);
}
}
};
}
public setCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => void): void {
this._csiHandlers[this._identifier(id)] = [callback];
public setEscHandler(id: IFunctionIdentifier, handler: EscHandler): void {
this._escHandlers[this._identifier(id, [0x30, 0x7e])] = [handler];
}
public clearEscHandler(id: IFunctionIdentifier): void {
if (this._escHandlers[this._identifier(id, [0x30, 0x7e])]) delete this._escHandlers[this._identifier(id, [0x30, 0x7e])];
}
public setEscHandlerFallback(handler: EscFallbackHandler): void {
this._escHandlerFb = handler;
}
public setExecuteHandler(flag: string, handler: ExecuteHandler): void {
this._executeHandlers[flag.charCodeAt(0)] = handler;
}
public clearExecuteHandler(flag: string): void {
if (this._executeHandlers[flag.charCodeAt(0)]) delete this._executeHandlers[flag.charCodeAt(0)];
}
public setExecuteHandlerFallback(handler: ExecuteFallbackHandler): void {
this._executeHandlerFb = handler;
}
public addCsiHandler(id: IFunctionIdentifier, handler: CsiHandler): IDisposable {
const ident = this._identifier(id);
if (this._csiHandlers[ident] === undefined) {
this._csiHandlers[ident] = [];
}
const handlerList = this._csiHandlers[ident];
handlerList.push(handler);
return {
dispose: () => {
const handlerIndex = handlerList.indexOf(handler);
if (handlerIndex !== -1) {
handlerList.splice(handlerIndex, 1);
}
}
};
}
public setCsiHandler(id: IFunctionIdentifier, handler: CsiHandler): void {
this._csiHandlers[this._identifier(id)] = [handler];
}
public clearCsiHandler(id: IFunctionIdentifier): void {
if (this._csiHandlers[this._identifier(id)]) delete this._csiHandlers[this._identifier(id)];
@@ -377,45 +403,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._csiHandlerFb = callback;
}
public addEscHandler(id: IFunctionIdentifier, callback: EscHandler): IDisposable {
const ident = this._identifier(id, [0x30, 0x7e]);
if (this._escHandlers[ident] === undefined) {
this._escHandlers[ident] = [];
}
const handlerList = this._escHandlers[ident];
handlerList.push(callback);
return {
dispose: () => {
const handlerIndex = handlerList.indexOf(callback);
if (handlerIndex !== -1) {
handlerList.splice(handlerIndex, 1);
}
}
};
}
public setEscHandler(id: IFunctionIdentifier, callback: () => void): void {
this._escHandlers[this._identifier(id, [0x30, 0x7e])] = [callback];
}
public clearEscHandler(id: IFunctionIdentifier): void {
if (this._escHandlers[this._identifier(id, [0x30, 0x7e])]) delete this._escHandlers[this._identifier(id, [0x30, 0x7e])];
}
public setEscHandlerFallback(callback: (ident: number) => void): void {
this._escHandlerFb = callback;
}
public addOscHandler(ident: number, handler: IOscHandler): IDisposable {
return this._oscParser.addHandler(ident, handler);
}
public setOscHandler(ident: number, handler: IOscHandler): void {
this._oscParser.setHandler(ident, handler);
}
public clearOscHandler(ident: number): void {
this._oscParser.clearHandler(ident);
}
public setOscHandlerFallback(handler: OscFallbackHandler): void {
this._oscParser.setHandlerFallback(handler);
}
public addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
return this._dcsParser.addHandler(this._identifier(id), handler);
}
@@ -429,6 +416,19 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._dcsParser.setHandlerFallback(handler);
}
public addOscHandler(ident: number, handler: IOscHandler): IDisposable {
return this._oscParser.addHandler(ident, handler);
}
public setOscHandler(ident: number, handler: IOscHandler): void {
this._oscParser.setHandler(ident, handler);
}
public clearOscHandler(ident: number): void {
this._oscParser.clearHandler(ident);
}
public setOscHandlerFallback(handler: OscFallbackHandler): void {
this._oscParser.setHandlerFallback(handler);
}
public setErrorHandler(callback: (state: IParsingState) => IParsingState): void {
this._errorHandler = callback;
}
+80 -53
View File
@@ -61,66 +61,85 @@ export interface IParsingState {
abort: boolean;
}
export interface IHandlerCollection<T> {
[key: string]: T[];
}
export type CsiHandler = (params: IParams) => boolean | void;
export type EscHandler = () => boolean | void;
/**
* Command handler interfaces.
*/
/**
* DCS handler signature for EscapeSequenceParser.
* EscapeSequenceParser handles DCS commands via separate
* subparsers that get hook/unhooked and can handle
* arbitrary amount of data.
*
* On entering a DSC sequence `hook` is called by
* `EscapeSequenceParser`. Use it to initialize or reset
* states needed to handle the current DCS sequence.
* Note: A DCS parser is only instantiated once, therefore
* you cannot rely on the ctor to reinitialize state.
*
* EscapeSequenceParser will call `put` several times if the
* parsed data got split, therefore you might have to collect
* `data` until `unhook` is called.
* Note: `data` is borrowed, if you cannot process the data
* in chunks you have to copy it, doing otherwise will lead to
* data losses or corruption.
*
* `unhook` marks the end of the current DCS sequence. `success`
* indicates whether the command was aborted.
*/
* DCS handler types.
*/
export interface IDcsHandler {
/**
* Called when a DCS command starts.
* Prepare needed data structures here.
* Note: `params` is borrowed.
*/
hook(params: IParams): void;
/**
* Incoming payload chunk.
* Note: `params` is borrowed.
*/
put(data: Uint32Array, start: number, end: number): void;
/**
* End of DCS command. `success` indicates whether the
* command finished normally or got aborted, thus final
* execution of the command should depend on `success`.
* To save memory also cleanup data structures here.
*/
unhook(success: boolean): void | boolean;
}
export type DcsFallbackHandler = (ident: number, action: 'HOOK' | 'PUT' | 'UNHOOK', payload?: any) => void;
/**
* ESC handler types.
*/
export type EscHandler = () => boolean | void;
export type EscFallbackHandler = (identifier: number) => void;
/**
* EXECUTE handler types.
*/
export type ExecuteHandler = () => boolean | void;
export type ExecuteFallbackHandler = (ident: number) => void;
/**
* CSI handler types.
* Note: `params` is borrowed.
*/
export type CsiHandler = (params: IParams) => boolean | void;
export type CsiFallbackHandler = (ident: number, params: IParams) => void;
/**
* OSC handler types.
*/
export interface IOscHandler {
/**
* Announces start of this OSC command.
* Prepare needed data structures here.
*/
start(): void;
/**
* Incoming data chunk.
* Note: Data is borrowed.
*/
put(data: Uint32Array, start: number, end: number): void;
/**
* End of OSC command. `success` indicates whether the
* command finished normally or got aborted, thus execution
* of the command should depend on `success`.
* To save memory cleanup data structures in `.end`.
* command finished normally or got aborted, thus final
* execution of the command should depend on `success`.
* To save memory also cleanup data structures here.
*/
end(success: boolean): void | boolean;
}
export type OscFallbackHandler = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
/**
* PRINT handler types.
*/
export type PrintHandler = (data: Uint32Array, start: number, end: number) => void;
export type PrintFallbackHandler = PrintHandler;
/**
* EscapeSequenceParser interface.
*/
@@ -144,38 +163,42 @@ export interface IEscapeSequenceParser extends IDisposable {
parse(data: Uint32Array, length: number): void;
/**
* Get string from ident number.
* Get string from numercial function identifier `ident`.
* Useful in fallback handlers which expose the low level
* numcerical function identifier for debugging purposes.
* Note: A full back translation to `IFunctionIdentifier`
* is not implemented.
*/
identToString(ident: number): string;
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void;
setPrintHandler(handler: PrintHandler): void;
clearPrintHandler(): void;
setExecuteHandler(flag: string, callback: () => void): void;
clearExecuteHandler(flag: string): void;
setExecuteHandlerFallback(callback: (code: number) => void): void;
setCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => void): void;
clearCsiHandler(id: IFunctionIdentifier): void;
setCsiHandlerFallback(callback: (identifier: number, params: IParams) => void): void;
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
setEscHandler(id: IFunctionIdentifier, callback: () => void): void;
setEscHandler(id: IFunctionIdentifier, handler: EscHandler): void;
clearEscHandler(id: IFunctionIdentifier): void;
setEscHandlerFallback(callback: (identifier: number) => void): void;
setEscHandlerFallback(handler: EscFallbackHandler): void;
addEscHandler(id: IFunctionIdentifier, handler: EscHandler): IDisposable;
setOscHandler(ident: number, handler: IOscHandler): void;
clearOscHandler(ident: number): void;
setOscHandlerFallback(handler: OscFallbackHandler): void;
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
setExecuteHandler(flag: string, handler: ExecuteHandler): void;
clearExecuteHandler(flag: string): void;
setExecuteHandlerFallback(handler: ExecuteFallbackHandler): void;
setCsiHandler(id: IFunctionIdentifier, handler: CsiHandler): void;
clearCsiHandler(id: IFunctionIdentifier): void;
setCsiHandlerFallback(callback: CsiFallbackHandler): void;
addCsiHandler(id: IFunctionIdentifier, handler: CsiHandler): IDisposable;
setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void;
clearDcsHandler(id: IFunctionIdentifier): void;
setDcsHandlerFallback(handler: DcsFallbackHandler): void;
addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable;
setErrorHandler(callback: (state: IParsingState) => IParsingState): void;
setOscHandler(ident: number, handler: IOscHandler): void;
clearOscHandler(ident: number): void;
setOscHandlerFallback(handler: OscFallbackHandler): void;
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
setErrorHandler(handler: (state: IParsingState) => IParsingState): void;
clearErrorHandler(): void;
}
@@ -207,7 +230,7 @@ export interface IDcsParser extends ISubParser<IDcsHandler, DcsFallbackHandler>
* Interface to denote a specific ESC, CSI or DCS handler slot.
* The values are used to create an integer respresentation during handler
* regristation before passed to the subparsers as `ident`.
* The integer translation is made to allow faster key-value access
* The integer translation is made to allow a faster handler access
* in `EscapeSequenceParser.parse`.
*/
export interface IFunctionIdentifier {
@@ -215,3 +238,7 @@ export interface IFunctionIdentifier {
intermediates?: string;
final: string;
}
export interface IHandlerCollection<T> {
[key: string]: T[];
}