mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
support for multiple DCS handler
This commit is contained in:
+12
-3
@@ -57,7 +57,11 @@ class DECRQSS implements IDcsHandler {
|
||||
this._data = concat(this._data, data.subarray(start, end));
|
||||
}
|
||||
|
||||
unhook(): void {
|
||||
unhook(success: boolean): void {
|
||||
if (!success) {
|
||||
this._data = new Uint32Array(0);
|
||||
return;
|
||||
}
|
||||
const data = utf32ToString(this._data);
|
||||
this._data = new Uint32Array(0);
|
||||
switch (data) {
|
||||
@@ -155,8 +159,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
});
|
||||
this._parser.setOscHandlerFallback((identifier, action, data) => {
|
||||
this._logService.debug('Unknown OSC code: ', { identifier, action, data });
|
||||
}
|
||||
);
|
||||
});
|
||||
this._parser.setDcsHandlerFallback((identifier, action, payload) => {
|
||||
if (payload.params) {
|
||||
payload.params = payload.params.toArray();
|
||||
}
|
||||
this._logService.debug('Unknown DCS code: ', { identifier, action, payload });
|
||||
});
|
||||
|
||||
/**
|
||||
* print handler
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { IDcsHandler, IParams, ParamsArray, IHandlerCollection, IDcsParser, DcsFallbackHandler } from 'common/parser/Types';
|
||||
import { utf32ToString } from 'common/input/TextDecoder';
|
||||
|
||||
|
||||
export class DcsParser implements IDcsParser {
|
||||
private _handlers: IHandlerCollection<IDcsHandler> = Object.create(null);
|
||||
private _active: IDcsHandler[] = [];
|
||||
private _collectAndFlag: string = '';
|
||||
private _handlerFb: DcsFallbackHandler = () => {};
|
||||
public dispose(): void {
|
||||
this._handlers = Object.create(null);
|
||||
this._handlerFb = () => {};
|
||||
}
|
||||
public addDcsHandler(collectAndFlag: string, handler: IDcsHandler): IDisposable {
|
||||
if (this._handlers[collectAndFlag] === undefined) {
|
||||
this._handlers[collectAndFlag] = [];
|
||||
}
|
||||
const handlerList = this._handlers[collectAndFlag];
|
||||
handlerList.push(handler);
|
||||
return {
|
||||
dispose: () => {
|
||||
const handlerIndex = handlerList.indexOf(handler);
|
||||
if (handlerIndex !== -1) {
|
||||
handlerList.splice(handlerIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
public setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void {
|
||||
this._handlers[collectAndFlag] = [handler];
|
||||
}
|
||||
public clearDcsHandler(collectAndFlag: string): void {
|
||||
if (this._handlers[collectAndFlag]) delete this._handlers[collectAndFlag];
|
||||
}
|
||||
public setOscHandlerFallback(handler: DcsFallbackHandler): void {
|
||||
this._handlerFb = handler;
|
||||
}
|
||||
public reset(): void {
|
||||
if (this._active.length) {
|
||||
this.unhook(false);
|
||||
}
|
||||
this._active = [];
|
||||
this._collectAndFlag = '';
|
||||
}
|
||||
public hook(collect: string, params: IParams, flag: number): void {
|
||||
this._collectAndFlag = collect + String.fromCharCode(flag);
|
||||
this._active = this._handlers[this._collectAndFlag] || [];
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._collectAndFlag, 'HOOK', {collect, params, flag});
|
||||
} else {
|
||||
for (let j = this._active.length - 1; j >= 0; j--) {
|
||||
this._active[j].hook(collect, params, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
public put(data: Uint32Array, start: number, end: number): void {
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._collectAndFlag, 'PUT', utf32ToString(data, start, end));
|
||||
} else {
|
||||
for (let j = this._active.length - 1; j >= 0; j--) {
|
||||
this._active[j].put(data, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
public unhook(success: boolean): void {
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._collectAndFlag, 'UNHOOK', success);
|
||||
} else {
|
||||
let j = this._active.length - 1;
|
||||
for (; j >= 0; j--) {
|
||||
if (this._active[j].unhook(success) !== false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
j--;
|
||||
// cleanup left over handlers
|
||||
for (; j >= 0; j--) {
|
||||
this._active[j].unhook(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DcsHandlerFactory implements IDcsHandler {
|
||||
private _data = '';
|
||||
private _params: IParams | undefined;
|
||||
constructor(private _handler: (params: ParamsArray, data: string) => any) {}
|
||||
public hook(collect: string, params: IParams, flag: number): void {
|
||||
this._params = params.clone();
|
||||
this._data = '';
|
||||
}
|
||||
public put(data: Uint32Array, start: number, end: number): void {
|
||||
this._data += utf32ToString(data, start, end);
|
||||
}
|
||||
public unhook(success: boolean): any {
|
||||
let ret;
|
||||
if (success) {
|
||||
ret = this._handler(this._params ? this._params.toArray() : [], this._data);
|
||||
}
|
||||
this._params = undefined;
|
||||
this._data = '';
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IDcsHandler, IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandler } from 'common/parser/Types';
|
||||
import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandler } from 'common/parser/Types';
|
||||
import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from 'common/parser/EscapeSequenceParser';
|
||||
import * as chai from 'chai';
|
||||
import { StringToUtf32, stringFromCodePoint, utf32ToString } from 'common/input/TextDecoder';
|
||||
@@ -77,9 +77,6 @@ class TestEscapeSequenceParser extends EscapeSequenceParser {
|
||||
public set collect(value: string) {
|
||||
this._collect = value;
|
||||
}
|
||||
public mockActiveDcsHandler(): void {
|
||||
this._activeDcsHandler = this._dcsHandlerFb;
|
||||
}
|
||||
public mockOscParser(): void {
|
||||
this._oscParser = oscPutParser;
|
||||
}
|
||||
@@ -116,11 +113,7 @@ const testTerminal: any = {
|
||||
actionDCSHook: function (collect: string, params: IParams, flag: string): void {
|
||||
this.calls.push(['dcs hook', collect, params.toArray(), flag]);
|
||||
},
|
||||
actionDCSPrint: function (data: Uint32Array, start: number, end: number): void {
|
||||
let s = '';
|
||||
for (let i = start; i < end; ++i) {
|
||||
s += stringFromCodePoint(data[i]);
|
||||
}
|
||||
actionDCSPrint: function (s: string): void {
|
||||
this.calls.push(['dcs put', s]);
|
||||
},
|
||||
actionDCSUnhook: function (): void {
|
||||
@@ -128,19 +121,6 @@ const testTerminal: any = {
|
||||
}
|
||||
};
|
||||
|
||||
// dcs handler to map dcs actions into the test object `testTerminal`
|
||||
class DcsTest implements IDcsHandler {
|
||||
hook(collect: string, params: IParams, flag: number): void {
|
||||
testTerminal.actionDCSHook(collect, params, String.fromCharCode(flag));
|
||||
}
|
||||
put(data: Uint32Array, start: number, end: number): void {
|
||||
testTerminal.actionDCSPrint(data, start, end);
|
||||
}
|
||||
unhook(): void {
|
||||
testTerminal.actionDCSUnhook();
|
||||
}
|
||||
}
|
||||
|
||||
const states: number[] = [
|
||||
ParserState.GROUND,
|
||||
ParserState.ESCAPE,
|
||||
@@ -176,7 +156,18 @@ testParser.setOscHandlerFallback((identifier, action, data) => {
|
||||
if (identifier === -1) testTerminal.actionOSC(data); // handle error condition silently
|
||||
else if (action === 'END') testTerminal.actionOSC('' + identifier + ';' + data); // collect only data at END
|
||||
});
|
||||
testParser.setDcsHandlerFallback(new DcsTest());
|
||||
testParser.setDcsHandlerFallback((collectAndFlag, action, payload) => {
|
||||
switch (action) {
|
||||
case 'HOOK':
|
||||
testTerminal.actionDCSHook(payload.collect, payload.params, String.fromCharCode(payload.flag));
|
||||
break;
|
||||
case 'PUT':
|
||||
testTerminal.actionDCSPrint(payload);
|
||||
break;
|
||||
case 'UNHOOK':
|
||||
testTerminal.actionDCSUnhook();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// translate string based parse calls into typed array based
|
||||
@@ -987,7 +978,6 @@ describe('EscapeSequenceParser', function (): void {
|
||||
puts = puts.concat(r(0x20, 0x7f));
|
||||
for (let i = 0; i < puts.length; ++i) {
|
||||
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
||||
parser.mockActiveDcsHandler();
|
||||
parse(parser, puts[i]);
|
||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||
testTerminal.compare([['dcs put', puts[i]]]);
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandler, OscFallbackHandler, IOscParser, EscHandler } from 'common/parser/Types';
|
||||
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandler, OscFallbackHandler, IOscParser, EscHandler, IDcsParser, DcsFallbackHandler } from 'common/parser/Types';
|
||||
import { ParserState, ParserAction } from 'common/parser/Constants';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { fill } from 'common/TypedArrayUtils';
|
||||
import { Params } from 'common/parser/Params';
|
||||
import { OscParser } from 'common/parser/OscParser';
|
||||
import { DcsParser } from 'common/parser/DcsParser';
|
||||
|
||||
/**
|
||||
* Table values are generated like this:
|
||||
@@ -196,14 +197,6 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||
return table;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Dummy DCS handler as default fallback.
|
||||
*/
|
||||
class DcsDummy implements IDcsHandler {
|
||||
hook(collect: string, params: IParams, flag: number): void { }
|
||||
put(data: Uint32Array, start: number, end: number): void { }
|
||||
unhook(): void { }
|
||||
}
|
||||
|
||||
/**
|
||||
* EscapeSequenceParser.
|
||||
@@ -242,8 +235,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
protected _csiHandlers: IHandlerCollection<CsiHandler>;
|
||||
protected _escHandlers: IHandlerCollection<EscHandler>;
|
||||
protected _oscParser: IOscParser;
|
||||
protected _dcsHandlers: any;
|
||||
protected _activeDcsHandler: IDcsHandler;
|
||||
protected _dcsParser: IDcsParser;
|
||||
protected _errorHandler: (state: IParsingState) => IParsingState;
|
||||
|
||||
// fallback handlers
|
||||
@@ -251,7 +243,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
protected _executeHandlerFb: (code: number) => void;
|
||||
protected _csiHandlerFb: (collect: string, params: IParams, flag: number) => void;
|
||||
protected _escHandlerFb: (collect: string, flag: number) => void;
|
||||
protected _dcsHandlerFb: IDcsHandler;
|
||||
protected _errorHandlerFb: (state: IParsingState) => IParsingState;
|
||||
|
||||
constructor(readonly TRANSITIONS: TransitionTable = VT500_TRANSITION_TABLE) {
|
||||
@@ -269,15 +260,13 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._executeHandlerFb = (code: number): void => { };
|
||||
this._csiHandlerFb = (collect: string, params: IParams, flag: number): void => { };
|
||||
this._escHandlerFb = (collect: string, flag: number): void => { };
|
||||
this._dcsHandlerFb = new DcsDummy();
|
||||
this._errorHandlerFb = (state: IParsingState): IParsingState => state;
|
||||
this._printHandler = this._printHandlerFb;
|
||||
this._executeHandlers = Object.create(null);
|
||||
this._csiHandlers = Object.create(null);
|
||||
this._escHandlers = Object.create(null);
|
||||
this._oscParser = new OscParser();
|
||||
this._dcsHandlers = Object.create(null);
|
||||
this._activeDcsHandler = this._dcsHandlerFb;
|
||||
this._dcsParser = new DcsParser();
|
||||
this._errorHandler = this._errorHandlerFb;
|
||||
|
||||
// swallow 7bit ST (ESC+\)
|
||||
@@ -287,9 +276,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
public dispose(): void {
|
||||
this._executeHandlers = null;
|
||||
this._escHandlers = Object.create(null);
|
||||
this._dcsHandlers = null;
|
||||
this._activeDcsHandler = new DcsDummy();
|
||||
this._oscParser.dispose();
|
||||
this._dcsParser.dispose();
|
||||
}
|
||||
|
||||
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void {
|
||||
@@ -373,19 +361,17 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._oscParser.setOscHandlerFallback(handler);
|
||||
}
|
||||
|
||||
addDcsHandler(collectAndFlag: string, handler: IDcsHandler): IDisposable {
|
||||
return this._dcsParser.addDcsHandler(collectAndFlag, handler);
|
||||
}
|
||||
setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void {
|
||||
this._dcsHandlers[collectAndFlag] = handler;
|
||||
this._dcsParser.setDcsHandler(collectAndFlag, handler);
|
||||
}
|
||||
clearDcsHandler(collectAndFlag: string): void {
|
||||
if (this._dcsHandlers[collectAndFlag]) delete this._dcsHandlers[collectAndFlag];
|
||||
this._dcsParser.clearDcsHandler(collectAndFlag);
|
||||
}
|
||||
setDcsHandlerFallback(handler: IDcsHandler): void {
|
||||
if (this._activeDcsHandler === this._dcsHandlerFb) {
|
||||
this._dcsHandlerFb = handler;
|
||||
this._activeDcsHandler = handler;
|
||||
} else {
|
||||
this._dcsHandlerFb = handler;
|
||||
}
|
||||
setDcsHandlerFallback(handler: DcsFallbackHandler): void {
|
||||
this._dcsParser.setOscHandlerFallback(handler);
|
||||
}
|
||||
|
||||
setErrorHandler(callback: (state: IParsingState) => IParsingState): void {
|
||||
@@ -398,10 +384,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
reset(): void {
|
||||
this.currentState = this.initialState;
|
||||
this._oscParser.reset();
|
||||
this._dcsParser.reset();
|
||||
this._params.reset();
|
||||
this._params.addParam(0); // ZDM
|
||||
this._collect = '';
|
||||
this._activeDcsHandler = this._dcsHandlerFb;
|
||||
// this._activeDcsHandler = this._dcsHandlerFb;
|
||||
this.precedingCodepoint = 0;
|
||||
}
|
||||
|
||||
@@ -424,10 +411,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
let transition = 0;
|
||||
let currentState = this.currentState;
|
||||
const osc = this._oscParser;
|
||||
const dcs = this._dcsParser;
|
||||
let collect = this._collect;
|
||||
const params = this._params;
|
||||
const table: Uint8Array = this.TRANSITIONS.table;
|
||||
let dcsHandler: IDcsHandler = this._activeDcsHandler;
|
||||
// let dcsHandler: IDcsHandler = this._activeDcsHandler;
|
||||
let callback: Function | null = null;
|
||||
|
||||
// process input string
|
||||
@@ -535,7 +523,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
if (jj < 0) {
|
||||
this._escHandlerFb(collect, code);
|
||||
}
|
||||
|
||||
this.precedingCodepoint = 0;
|
||||
break;
|
||||
case ParserAction.CLEAR:
|
||||
@@ -545,24 +532,21 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
collect = '';
|
||||
break;
|
||||
case ParserAction.DCS_HOOK:
|
||||
dcsHandler = this._dcsHandlers[collect + String.fromCharCode(code)];
|
||||
if (!dcsHandler) dcsHandler = this._dcsHandlerFb;
|
||||
dcsHandler.hook(collect, params, code);
|
||||
dcs.hook(collect, params, code);
|
||||
break;
|
||||
case ParserAction.DCS_PUT:
|
||||
// inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
|
||||
// unhook triggered by: 0x1b, 0x9c
|
||||
for (let j = i + 1; ; ++j) {
|
||||
if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
|
||||
dcsHandler.put(data, i, j);
|
||||
dcs.put(data, i, j);
|
||||
i = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ParserAction.DCS_UNHOOK:
|
||||
dcsHandler.unhook();
|
||||
dcsHandler = this._dcsHandlerFb;
|
||||
dcs.unhook(true); // FIXME: apply abort vs. success exit rules
|
||||
if (code === 0x1b) transition |= ParserState.ESCAPE;
|
||||
osc.reset();
|
||||
params.reset();
|
||||
@@ -598,10 +582,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
|
||||
// save non pushable buffers
|
||||
this._collect = collect;
|
||||
this._params = params;
|
||||
|
||||
// save active dcs handler reference
|
||||
this._activeDcsHandler = dcsHandler;
|
||||
|
||||
// save state
|
||||
this.currentState = currentState;
|
||||
|
||||
@@ -42,7 +42,8 @@ export class OscParser extends Disposable {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._handlers = {};
|
||||
this._handlers = Object.create(null);
|
||||
this._handlerFb = () => {};
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
|
||||
Vendored
+19
-4
@@ -89,15 +89,16 @@ export type EscHandler = () => boolean | void;
|
||||
* 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.
|
||||
* `unhook` marks the end of the current DCS sequence. `success`
|
||||
* indicates whether the command was aborted.
|
||||
*/
|
||||
export interface IDcsHandler {
|
||||
hook(collect: string, params: IParams, flag: number): void;
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
unhook(): void;
|
||||
unhook(success: boolean): void | boolean;
|
||||
}
|
||||
|
||||
export type OscFallbackHandler = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
|
||||
export type DcsFallbackHandler = (collectAndFlag: string, action: 'HOOK' | 'PUT' | 'UNHOOK', payload?: any) => void;
|
||||
|
||||
export interface IOscHandler {
|
||||
/**
|
||||
@@ -120,6 +121,8 @@ export interface IOscHandler {
|
||||
end(success: boolean): void | boolean;
|
||||
}
|
||||
|
||||
export type OscFallbackHandler = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
|
||||
|
||||
/**
|
||||
* EscapeSequenceParser interface.
|
||||
*/
|
||||
@@ -166,7 +169,8 @@ export interface IEscapeSequenceParser extends IDisposable {
|
||||
|
||||
setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void;
|
||||
clearDcsHandler(collectAndFlag: string): void;
|
||||
setDcsHandlerFallback(handler: IDcsHandler): void;
|
||||
setDcsHandlerFallback(handler: DcsFallbackHandler): void;
|
||||
addDcsHandler(collectAndFlag: string, handler: IDcsHandler): IDisposable;
|
||||
|
||||
setErrorHandler(callback: (state: IParsingState) => IParsingState): void;
|
||||
clearErrorHandler(): void;
|
||||
@@ -182,3 +186,14 @@ export interface IOscParser extends IDisposable {
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
end(success: boolean): void;
|
||||
}
|
||||
|
||||
export interface IDcsParser extends IDisposable {
|
||||
addDcsHandler(collectAndFlag: string, handler: IDcsHandler): IDisposable;
|
||||
setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void;
|
||||
clearDcsHandler(collectAndFlag: string): void;
|
||||
setOscHandlerFallback(handler: DcsFallbackHandler): void;
|
||||
reset(): void;
|
||||
hook(collect: string, params: IParams, flag: number): void;
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
unhook(success: boolean): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user