File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,544 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array filled with numbers between the low and high parameters (right exclusive).
|
||||||
|
* @param low The low number.
|
||||||
|
* @param high The high number.
|
||||||
|
*/
|
||||||
|
function r(low: number, high: number): number[] {
|
||||||
|
let c = high - low;
|
||||||
|
let arr = new Array(c);
|
||||||
|
while (c--) {
|
||||||
|
arr[c] = --high;
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition table for EscapeSequenceParser.
|
||||||
|
* NOTE: data in the underlying table is packed like this:
|
||||||
|
* currentState << 8 | characterCode --> action << 4 | nextState
|
||||||
|
*/
|
||||||
|
export class TransitionTable {
|
||||||
|
public table: Uint8Array | number[];
|
||||||
|
|
||||||
|
constructor(length: number) {
|
||||||
|
this.table = (typeof Uint8Array === 'undefined')
|
||||||
|
? new Array(length)
|
||||||
|
: new Uint8Array(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a transition to the transition table.
|
||||||
|
* @param code input character code
|
||||||
|
* @param state current parser state
|
||||||
|
* @param action parser action to be done
|
||||||
|
* @param next next parser state
|
||||||
|
*/
|
||||||
|
add(code: number, state: number, action: number | null, next: number | null): void {
|
||||||
|
this.table[state << 8 | code] = ((action | 0) << 4) | ((next === undefined) ? state : next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add transitions for multiple input character codes.
|
||||||
|
* @param codes input character code array
|
||||||
|
* @param state current parser state
|
||||||
|
* @param action parser action to be done
|
||||||
|
* @param next next parser state
|
||||||
|
*/
|
||||||
|
addMany(codes: number[], state: number, action: number | null, next: number | null): void {
|
||||||
|
for (let i = 0; i < codes.length; i++) {
|
||||||
|
this.add(codes[i], state, action, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default definitions for the VT500_TRANSITION_TABLE.
|
||||||
|
*/
|
||||||
|
let PRINTABLES = r(0x20, 0x7f);
|
||||||
|
let EXECUTABLES = r(0x00, 0x18);
|
||||||
|
EXECUTABLES.push(0x19);
|
||||||
|
EXECUTABLES.concat(r(0x1c, 0x20));
|
||||||
|
const DEFAULT_TRANSITION = ParserAction.ERROR << 4 | ParserState.GROUND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VT500 compatible transition table.
|
||||||
|
* Taken from https://vt100.net/emu/dec_ansi_parser.
|
||||||
|
*/
|
||||||
|
export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
||||||
|
let table: TransitionTable = new TransitionTable(4095);
|
||||||
|
|
||||||
|
let states: number[] = r(ParserState.GROUND, ParserState.DCS_PASSTHROUGH + 1);
|
||||||
|
let state: any;
|
||||||
|
|
||||||
|
// table with default transition [any] --> DEFAULT_TRANSITION
|
||||||
|
for (state in states) {
|
||||||
|
// NOTE: table lookup is capped at 0xa0 in parse to keep the table small
|
||||||
|
for (let code = 0; code < 160; ++code) {
|
||||||
|
table.add(code, state, ParserAction.ERROR, ParserState.GROUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// printables
|
||||||
|
table.addMany(PRINTABLES, ParserState.GROUND, ParserAction.PRINT, ParserState.GROUND);
|
||||||
|
// global anywhere rules
|
||||||
|
for (state in states) {
|
||||||
|
table.addMany([0x18, 0x1a, 0x99, 0x9a], state, ParserAction.EXECUTE, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x80, 0x90), state, ParserAction.EXECUTE, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x90, 0x98), state, ParserAction.EXECUTE, ParserState.GROUND);
|
||||||
|
table.add(0x9c, state, ParserAction.IGNORE, ParserState.GROUND); // ST as terminator
|
||||||
|
table.add(0x1b, state, ParserAction.CLEAR, ParserState.ESCAPE); // ESC
|
||||||
|
table.add(0x9d, state, ParserAction.OSC_START, ParserState.OSC_STRING); // OSC
|
||||||
|
table.addMany([0x98, 0x9e, 0x9f], state, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
|
table.add(0x9b, state, ParserAction.CLEAR, ParserState.CSI_ENTRY); // CSI
|
||||||
|
table.add(0x90, state, ParserAction.CLEAR, ParserState.DCS_ENTRY); // DCS
|
||||||
|
}
|
||||||
|
// rules for executables and 7f
|
||||||
|
table.addMany(EXECUTABLES, ParserState.GROUND, ParserAction.EXECUTE, ParserState.GROUND);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.ESCAPE, ParserAction.EXECUTE, ParserState.ESCAPE);
|
||||||
|
table.add(0x7f, ParserState.ESCAPE, ParserAction.IGNORE, ParserState.ESCAPE);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.OSC_STRING, ParserAction.IGNORE, ParserState.OSC_STRING);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.CSI_ENTRY, ParserAction.EXECUTE, ParserState.CSI_ENTRY);
|
||||||
|
table.add(0x7f, ParserState.CSI_ENTRY, ParserAction.IGNORE, ParserState.CSI_ENTRY);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.CSI_PARAM, ParserAction.EXECUTE, ParserState.CSI_PARAM);
|
||||||
|
table.add(0x7f, ParserState.CSI_PARAM, ParserAction.IGNORE, ParserState.CSI_PARAM);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.CSI_IGNORE, ParserAction.EXECUTE, ParserState.CSI_IGNORE);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.CSI_INTERMEDIATE, ParserAction.EXECUTE, ParserState.CSI_INTERMEDIATE);
|
||||||
|
table.add(0x7f, ParserState.CSI_INTERMEDIATE, ParserAction.IGNORE, ParserState.CSI_INTERMEDIATE);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.ESCAPE_INTERMEDIATE, ParserAction.EXECUTE, ParserState.ESCAPE_INTERMEDIATE);
|
||||||
|
table.add(0x7f, ParserState.ESCAPE_INTERMEDIATE, ParserAction.IGNORE, ParserState.ESCAPE_INTERMEDIATE);
|
||||||
|
// osc
|
||||||
|
table.add(0x5d, ParserState.ESCAPE, ParserAction.OSC_START, ParserState.OSC_STRING);
|
||||||
|
table.addMany(PRINTABLES, ParserState.OSC_STRING, ParserAction.OSC_PUT, ParserState.OSC_STRING);
|
||||||
|
table.add(0x7f, ParserState.OSC_STRING, ParserAction.OSC_PUT, ParserState.OSC_STRING);
|
||||||
|
table.addMany([0x9c, 0x1b, 0x18, 0x1a, 0x07], ParserState.OSC_STRING, ParserAction.OSC_END, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x1c, 0x20), ParserState.OSC_STRING, ParserAction.IGNORE, ParserState.OSC_STRING);
|
||||||
|
// sos/pm/apc does nothing
|
||||||
|
table.addMany([0x58, 0x5e, 0x5f], ParserState.ESCAPE, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
|
table.addMany(PRINTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
|
table.add(0x9c, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.GROUND);
|
||||||
|
// csi entries
|
||||||
|
table.add(0x5b, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.CSI_ENTRY);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.CSI_ENTRY, ParserAction.CSI_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x30, 0x3a), ParserState.CSI_ENTRY, ParserAction.PARAM, ParserState.CSI_PARAM);
|
||||||
|
table.add(0x3b, ParserState.CSI_ENTRY, ParserAction.PARAM, ParserState.CSI_PARAM);
|
||||||
|
table.addMany([0x3c, 0x3d, 0x3e, 0x3f], ParserState.CSI_ENTRY, ParserAction.COLLECT, ParserState.CSI_PARAM);
|
||||||
|
table.addMany(r(0x30, 0x3a), ParserState.CSI_PARAM, ParserAction.PARAM, ParserState.CSI_PARAM);
|
||||||
|
table.add(0x3b, ParserState.CSI_PARAM, ParserAction.PARAM, ParserState.CSI_PARAM);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.CSI_PARAM, ParserAction.CSI_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany([0x3a, 0x3c, 0x3d, 0x3e, 0x3f], ParserState.CSI_PARAM, ParserAction.IGNORE, ParserState.CSI_IGNORE);
|
||||||
|
table.addMany(r(0x20, 0x40), ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.CSI_IGNORE);
|
||||||
|
table.add(0x7f, ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.CSI_IGNORE);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.GROUND);
|
||||||
|
table.add(0x3a, ParserState.CSI_ENTRY, ParserAction.IGNORE, ParserState.CSI_IGNORE);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.CSI_ENTRY, ParserAction.COLLECT, ParserState.CSI_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.CSI_INTERMEDIATE, ParserAction.COLLECT, ParserState.CSI_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x30, 0x40), ParserState.CSI_INTERMEDIATE, ParserAction.IGNORE, ParserState.CSI_IGNORE);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.CSI_INTERMEDIATE, ParserAction.CSI_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.CSI_PARAM, ParserAction.COLLECT, ParserState.CSI_INTERMEDIATE);
|
||||||
|
// esc_intermediate
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.ESCAPE, ParserAction.COLLECT, ParserState.ESCAPE_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.ESCAPE_INTERMEDIATE, ParserAction.COLLECT, ParserState.ESCAPE_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x30, 0x7f), ParserState.ESCAPE_INTERMEDIATE, ParserAction.ESC_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x30, 0x50), ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x51, 0x58), ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany([0x59, 0x5a, 0x5c], ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND);
|
||||||
|
table.addMany(r(0x60, 0x7f), ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND);
|
||||||
|
// dcs entry
|
||||||
|
table.add(0x50, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.DCS_ENTRY);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_ENTRY);
|
||||||
|
table.add(0x7f, ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_ENTRY);
|
||||||
|
table.addMany(r(0x1c, 0x20), ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_ENTRY);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.DCS_ENTRY, ParserAction.COLLECT, ParserState.DCS_INTERMEDIATE);
|
||||||
|
table.add(0x3a, ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||||
|
table.addMany(r(0x30, 0x3a), ParserState.DCS_ENTRY, ParserAction.PARAM, ParserState.DCS_PARAM);
|
||||||
|
table.add(0x3b, ParserState.DCS_ENTRY, ParserAction.PARAM, ParserState.DCS_PARAM);
|
||||||
|
table.addMany([0x3c, 0x3d, 0x3e, 0x3f], ParserState.DCS_ENTRY, ParserAction.COLLECT, ParserState.DCS_PARAM);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||||
|
table.addMany(r(0x20, 0x80), ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||||
|
table.addMany(r(0x1c, 0x20), ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_PARAM);
|
||||||
|
table.add(0x7f, ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_PARAM);
|
||||||
|
table.addMany(r(0x1c, 0x20), ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_PARAM);
|
||||||
|
table.addMany(r(0x30, 0x3a), ParserState.DCS_PARAM, ParserAction.PARAM, ParserState.DCS_PARAM);
|
||||||
|
table.add(0x3b, ParserState.DCS_PARAM, ParserAction.PARAM, ParserState.DCS_PARAM);
|
||||||
|
table.addMany([0x3a, 0x3c, 0x3d, 0x3e, 0x3f], ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.DCS_PARAM, ParserAction.COLLECT, ParserState.DCS_INTERMEDIATE);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_INTERMEDIATE);
|
||||||
|
table.add(0x7f, ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x1c, 0x20), ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x20, 0x30), ParserState.DCS_INTERMEDIATE, ParserAction.COLLECT, ParserState.DCS_INTERMEDIATE);
|
||||||
|
table.addMany(r(0x30, 0x40), ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_IGNORE);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.DCS_INTERMEDIATE, ParserAction.DCS_HOOK, ParserState.DCS_PASSTHROUGH);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.DCS_PARAM, ParserAction.DCS_HOOK, ParserState.DCS_PASSTHROUGH);
|
||||||
|
table.addMany(r(0x40, 0x7f), ParserState.DCS_ENTRY, ParserAction.DCS_HOOK, ParserState.DCS_PASSTHROUGH);
|
||||||
|
table.addMany(EXECUTABLES, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH);
|
||||||
|
table.addMany(PRINTABLES, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH);
|
||||||
|
table.add(0x7f, ParserState.DCS_PASSTHROUGH, ParserAction.IGNORE, ParserState.DCS_PASSTHROUGH);
|
||||||
|
table.addMany([0x1b, 0x9c], ParserState.DCS_PASSTHROUGH, ParserAction.DCS_UNHOOK, ParserState.GROUND);
|
||||||
|
return table;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy DCS handler as default fallback.
|
||||||
|
*/
|
||||||
|
class DcsDummy implements IDcsHandler {
|
||||||
|
hook(collect: string, params: number[], flag: number): void { }
|
||||||
|
put(data: string, start: number, end: number): void { }
|
||||||
|
unhook(): void { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EscapeSequenceParser.
|
||||||
|
* This class implements the ANSI/DEC compatible parser described by
|
||||||
|
* Paul Williams (https://vt100.net/emu/dec_ansi_parser).
|
||||||
|
* To implement custom ANSI compliant escape sequences it is not needed to
|
||||||
|
* alter this parser, instead consider registering a custom handler.
|
||||||
|
* For non ANSI compliant sequences change the transition table with
|
||||||
|
* the optional `transitions` contructor argument and
|
||||||
|
* reimplement the `parse` method.
|
||||||
|
* NOTE: The parameter element notation is currently not supported.
|
||||||
|
* TODO: implement error recovery hook via error handler return values
|
||||||
|
*/
|
||||||
|
export class EscapeSequenceParser implements IEscapeSequenceParser {
|
||||||
|
public initialState: number;
|
||||||
|
public currentState: number;
|
||||||
|
|
||||||
|
// buffers over several parse calls
|
||||||
|
protected _osc: string;
|
||||||
|
protected _params: number[];
|
||||||
|
protected _collect: string;
|
||||||
|
|
||||||
|
// handler lookup containers
|
||||||
|
protected _printHandler: (data: string, start: number, end: number) => void;
|
||||||
|
protected _executeHandlers: any;
|
||||||
|
protected _csiHandlers: any;
|
||||||
|
protected _escHandlers: any;
|
||||||
|
protected _oscHandlers: any;
|
||||||
|
protected _dcsHandlers: any;
|
||||||
|
protected _activeDcsHandler: IDcsHandler | null;
|
||||||
|
protected _errorHandler: (state: IParsingState) => IParsingState;
|
||||||
|
|
||||||
|
// fallback handlers
|
||||||
|
protected _printHandlerFb: (data: string, start: number, end: number) => void;
|
||||||
|
protected _executeHandlerFb: (code: number) => void;
|
||||||
|
protected _csiHandlerFb: (collect: string, params: number[], flag: number) => void;
|
||||||
|
protected _escHandlerFb: (collect: string, flag: number) => void;
|
||||||
|
protected _oscHandlerFb: (identifier: number, data: string) => void;
|
||||||
|
protected _dcsHandlerFb: IDcsHandler;
|
||||||
|
protected _errorHandlerFb: (state: IParsingState) => IParsingState;
|
||||||
|
|
||||||
|
constructor(readonly transitions: TransitionTable = VT500_TRANSITION_TABLE) {
|
||||||
|
this.initialState = ParserState.GROUND;
|
||||||
|
this.currentState = this.initialState;
|
||||||
|
this._osc = '';
|
||||||
|
this._params = [0];
|
||||||
|
this._collect = '';
|
||||||
|
|
||||||
|
// set default fallback handlers and handler lookup containers
|
||||||
|
this._printHandlerFb = (data, start, end): void => { };
|
||||||
|
this._executeHandlerFb = (code: number): void => { };
|
||||||
|
this._csiHandlerFb = (collect: string, params: number[], flag: number): void => { };
|
||||||
|
this._escHandlerFb = (collect: string, flag: number): void => { };
|
||||||
|
this._oscHandlerFb = (identifier: number, data: string): 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._oscHandlers = Object.create(null);
|
||||||
|
this._dcsHandlers = Object.create(null);
|
||||||
|
this._activeDcsHandler = null;
|
||||||
|
this._errorHandler = this._errorHandlerFb;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrintHandler(callback: (data: string, start: number, end: number) => void): void {
|
||||||
|
this._printHandler = callback;
|
||||||
|
}
|
||||||
|
clearPrintHandler(): void {
|
||||||
|
this._printHandler = this._printHandlerFb;
|
||||||
|
}
|
||||||
|
|
||||||
|
setExecuteHandler(flag: string, callback: () => void): void {
|
||||||
|
this._executeHandlers[flag.charCodeAt(0)] = callback;
|
||||||
|
}
|
||||||
|
clearExecuteHandler(flag: string): void {
|
||||||
|
if (this._executeHandlers[flag.charCodeAt(0)]) delete this._executeHandlers[flag.charCodeAt(0)];
|
||||||
|
}
|
||||||
|
setExecuteHandlerFallback(callback: (code: number) => void): void {
|
||||||
|
this._executeHandlerFb = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void {
|
||||||
|
this._csiHandlers[flag.charCodeAt(0)] = callback;
|
||||||
|
}
|
||||||
|
clearCsiHandler(flag: string): void {
|
||||||
|
if (this._csiHandlers[flag.charCodeAt(0)]) delete this._csiHandlers[flag.charCodeAt(0)];
|
||||||
|
}
|
||||||
|
setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void {
|
||||||
|
this._csiHandlerFb = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEscHandler(collectAndFlag: string, callback: () => void): void {
|
||||||
|
this._escHandlers[collectAndFlag] = callback;
|
||||||
|
}
|
||||||
|
clearEscHandler(collectAndFlag: string): void {
|
||||||
|
if (this._escHandlers[collectAndFlag]) delete this._escHandlers[collectAndFlag];
|
||||||
|
}
|
||||||
|
setEscHandlerFallback(callback: (collect: string, flag: number) => void): void {
|
||||||
|
this._escHandlerFb = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOscHandler(ident: number, callback: (data: string) => void): void {
|
||||||
|
this._oscHandlers[ident] = callback;
|
||||||
|
}
|
||||||
|
clearOscHandler(ident: number): void {
|
||||||
|
if (this._oscHandlers[ident]) delete this._oscHandlers[ident];
|
||||||
|
}
|
||||||
|
setOscHandlerFallback(callback: (identifier: number, data: string) => void): void {
|
||||||
|
this._oscHandlerFb = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void {
|
||||||
|
this._dcsHandlers[collectAndFlag] = handler;
|
||||||
|
}
|
||||||
|
clearDcsHandler(collectAndFlag: string): void {
|
||||||
|
if (this._dcsHandlers[collectAndFlag]) delete this._dcsHandlers[collectAndFlag];
|
||||||
|
}
|
||||||
|
setDcsHandlerFallback(handler: IDcsHandler): void {
|
||||||
|
this._dcsHandlerFb = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrorHandler(callback: (state: IParsingState) => IParsingState): void {
|
||||||
|
this._errorHandler = callback;
|
||||||
|
}
|
||||||
|
clearErrorHandler(): void {
|
||||||
|
this._errorHandler = this._errorHandlerFb;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.currentState = this.initialState;
|
||||||
|
this._osc = '';
|
||||||
|
this._params = [0];
|
||||||
|
this._collect = '';
|
||||||
|
this._activeDcsHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(data: string): void {
|
||||||
|
let code = 0;
|
||||||
|
let transition = 0;
|
||||||
|
let error = false;
|
||||||
|
let currentState = this.currentState;
|
||||||
|
let print = -1;
|
||||||
|
let dcs = -1;
|
||||||
|
let osc = this._osc;
|
||||||
|
let collect = this._collect;
|
||||||
|
let params = this._params;
|
||||||
|
const table: Uint8Array | number[] = this.transitions.table;
|
||||||
|
let dcsHandler: IDcsHandler | null = this._activeDcsHandler;
|
||||||
|
let callback: Function | null = null;
|
||||||
|
|
||||||
|
// process input string
|
||||||
|
const l = data.length;
|
||||||
|
for (let i = 0; i < l; ++i) {
|
||||||
|
code = data.charCodeAt(i);
|
||||||
|
|
||||||
|
// shortcut for most chars (print action)
|
||||||
|
if (currentState === ParserState.GROUND && code > 0x1f && code < 0x80) {
|
||||||
|
print = (~print) ? print : i;
|
||||||
|
do code = data.charCodeAt(++i);
|
||||||
|
while (i < l && code > 0x1f && code < 0x80);
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shortcut for CSI params
|
||||||
|
if (currentState === ParserState.CSI_PARAM && (code > 0x2f && code < 0x39)) {
|
||||||
|
params[params.length - 1] = params[params.length - 1] * 10 + code - 48;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal transition & action lookup
|
||||||
|
transition = (code < 0xa0) ? (table[currentState << 8 | code]) : DEFAULT_TRANSITION;
|
||||||
|
switch (transition >> 4) {
|
||||||
|
case ParserAction.PRINT:
|
||||||
|
print = (~print) ? print : i;
|
||||||
|
break;
|
||||||
|
case ParserAction.EXECUTE:
|
||||||
|
if (~print) {
|
||||||
|
this._printHandler(data, print, i);
|
||||||
|
print = -1;
|
||||||
|
}
|
||||||
|
callback = this._executeHandlers[code];
|
||||||
|
if (callback) callback();
|
||||||
|
else this._executeHandlerFb(code);
|
||||||
|
break;
|
||||||
|
case ParserAction.IGNORE:
|
||||||
|
// handle leftover print or dcs chars
|
||||||
|
if (~print) {
|
||||||
|
this._printHandler(data, print, i);
|
||||||
|
print = -1;
|
||||||
|
} else if (~dcs) {
|
||||||
|
dcsHandler.put(data, dcs, i);
|
||||||
|
dcs = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ParserAction.ERROR:
|
||||||
|
// chars higher than 0x9f are handled by this action
|
||||||
|
// to keep the transition table small
|
||||||
|
if (code > 0x9f) {
|
||||||
|
switch (currentState) {
|
||||||
|
case ParserState.GROUND:
|
||||||
|
print = (~print) ? print : i;
|
||||||
|
break;
|
||||||
|
case ParserState.OSC_STRING:
|
||||||
|
osc += String.fromCharCode(code);
|
||||||
|
transition |= ParserState.OSC_STRING;
|
||||||
|
break;
|
||||||
|
case ParserState.CSI_IGNORE:
|
||||||
|
transition |= ParserState.CSI_IGNORE;
|
||||||
|
break;
|
||||||
|
case ParserState.DCS_IGNORE:
|
||||||
|
transition |= ParserState.DCS_IGNORE;
|
||||||
|
break;
|
||||||
|
case ParserState.DCS_PASSTHROUGH:
|
||||||
|
dcs = (~dcs) ? dcs : i;
|
||||||
|
transition |= ParserState.DCS_PASSTHROUGH;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
// if we end up here a real error happened
|
||||||
|
if (error) {
|
||||||
|
let inject: IParsingState = this._errorHandler(
|
||||||
|
{
|
||||||
|
position: i,
|
||||||
|
code,
|
||||||
|
currentState,
|
||||||
|
print,
|
||||||
|
dcs,
|
||||||
|
osc,
|
||||||
|
collect,
|
||||||
|
params,
|
||||||
|
abort: false
|
||||||
|
});
|
||||||
|
if (inject.abort) return;
|
||||||
|
// TODO: inject return values
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ParserAction.CSI_DISPATCH:
|
||||||
|
callback = this._csiHandlers[code];
|
||||||
|
if (callback) callback(params, collect);
|
||||||
|
else this._csiHandlerFb(collect, params, code);
|
||||||
|
break;
|
||||||
|
case ParserAction.PARAM:
|
||||||
|
if (code === 0x3b) params.push(0);
|
||||||
|
else params[params.length - 1] = params[params.length - 1] * 10 + code - 48;
|
||||||
|
break;
|
||||||
|
case ParserAction.COLLECT:
|
||||||
|
collect += String.fromCharCode(code);
|
||||||
|
break;
|
||||||
|
case ParserAction.ESC_DISPATCH:
|
||||||
|
callback = this._escHandlers[collect + String.fromCharCode(code)];
|
||||||
|
if (callback) callback(collect, code);
|
||||||
|
else this._escHandlerFb(collect, code);
|
||||||
|
break;
|
||||||
|
case ParserAction.CLEAR:
|
||||||
|
if (~print) {
|
||||||
|
this._printHandler(data, print, i);
|
||||||
|
print = -1;
|
||||||
|
}
|
||||||
|
osc = '';
|
||||||
|
params = [0];
|
||||||
|
collect = '';
|
||||||
|
dcs = -1;
|
||||||
|
break;
|
||||||
|
case ParserAction.DCS_HOOK:
|
||||||
|
dcsHandler = this._dcsHandlers[collect + String.fromCharCode(code)];
|
||||||
|
if (!dcsHandler) dcsHandler = this._dcsHandlerFb;
|
||||||
|
dcsHandler.hook(collect, params, code);
|
||||||
|
break;
|
||||||
|
case ParserAction.DCS_PUT:
|
||||||
|
dcs = (~dcs) ? dcs : i;
|
||||||
|
break;
|
||||||
|
case ParserAction.DCS_UNHOOK:
|
||||||
|
if (dcsHandler) {
|
||||||
|
if (~dcs) dcsHandler.put(data, dcs, i);
|
||||||
|
dcsHandler.unhook();
|
||||||
|
dcsHandler = null;
|
||||||
|
}
|
||||||
|
if (code === 0x1b) transition |= ParserState.ESCAPE;
|
||||||
|
osc = '';
|
||||||
|
params = [0];
|
||||||
|
collect = '';
|
||||||
|
dcs = -1;
|
||||||
|
break;
|
||||||
|
case ParserAction.OSC_START:
|
||||||
|
if (~print) {
|
||||||
|
this._printHandler(data, print, i);
|
||||||
|
print = -1;
|
||||||
|
}
|
||||||
|
osc = '';
|
||||||
|
break;
|
||||||
|
case ParserAction.OSC_PUT:
|
||||||
|
osc += data.charAt(i);
|
||||||
|
break;
|
||||||
|
case ParserAction.OSC_END:
|
||||||
|
if (osc && code !== 0x18 && code !== 0x1a) {
|
||||||
|
// NOTE: OSC subparsing is not part of the original parser
|
||||||
|
// we do basic identifier parsing here to offer a jump table for OSC as well
|
||||||
|
let idx = osc.indexOf(';');
|
||||||
|
if (idx === -1) {
|
||||||
|
this._oscHandlerFb(-1, osc); // this is an error (malformed OSC)
|
||||||
|
} else {
|
||||||
|
// Note: NaN is not handled here
|
||||||
|
// either catch it with the fallback handler
|
||||||
|
// or with an explicit NaN OSC handler
|
||||||
|
let identifier = parseInt(osc.substring(0, idx));
|
||||||
|
let content = osc.substring(idx + 1);
|
||||||
|
callback = this._oscHandlers[identifier];
|
||||||
|
if (callback) callback(content);
|
||||||
|
else this._oscHandlerFb(identifier, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (code === 0x1b) transition |= ParserState.ESCAPE;
|
||||||
|
osc = '';
|
||||||
|
params = [0];
|
||||||
|
collect = '';
|
||||||
|
dcs = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentState = transition & 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// push leftover pushable buffers to terminal
|
||||||
|
if (currentState === ParserState.GROUND && ~print) {
|
||||||
|
this._printHandler(data, print, data.length);
|
||||||
|
} else if (currentState === ParserState.DCS_PASSTHROUGH && ~dcs && dcsHandler) {
|
||||||
|
dcsHandler.put(data, dcs, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save non pushable buffers
|
||||||
|
this._osc = osc;
|
||||||
|
this._collect = collect;
|
||||||
|
this._params = params;
|
||||||
|
|
||||||
|
// save active dcs handler reference
|
||||||
|
this._activeDcsHandler = dcsHandler;
|
||||||
|
|
||||||
|
// save state
|
||||||
|
this.currentState = currentState;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,3 +77,74 @@ export namespace C0 {
|
|||||||
/** Delete (Caret = ^?) */
|
/** Delete (Caret = ^?) */
|
||||||
export const DEL = '\x7f';
|
export const DEL = '\x7f';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* C1 control codes
|
||||||
|
* See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes
|
||||||
|
*/
|
||||||
|
export namespace C1 {
|
||||||
|
/** padding character */
|
||||||
|
export const PAD = '\x80';
|
||||||
|
/** High Octet Preset */
|
||||||
|
export const HOP = '\x81';
|
||||||
|
/** Break Permitted Here */
|
||||||
|
export const BPH = '\x82';
|
||||||
|
/** No Break Here */
|
||||||
|
export const NBH = '\x83';
|
||||||
|
/** Index */
|
||||||
|
export const IND = '\x84';
|
||||||
|
/** Next Line */
|
||||||
|
export const NEL = '\x85';
|
||||||
|
/** Start of Selected Area */
|
||||||
|
export const SSA = '\x86';
|
||||||
|
/** End of Selected Area */
|
||||||
|
export const ESA = '\x87';
|
||||||
|
/** Horizontal Tabulation Set */
|
||||||
|
export const HTS = '\x88';
|
||||||
|
/** Horizontal Tabulation With Justification */
|
||||||
|
export const HTJ = '\x89';
|
||||||
|
/** Vertical Tabulation Set */
|
||||||
|
export const VTS = '\x8a';
|
||||||
|
/** Partial Line Down */
|
||||||
|
export const PLD = '\x8b';
|
||||||
|
/** Partial Line Up */
|
||||||
|
export const PLU = '\x8c';
|
||||||
|
/** Reverse Index */
|
||||||
|
export const RI = '\x8d';
|
||||||
|
/** Single-Shift 2 */
|
||||||
|
export const SS2 = '\x8e';
|
||||||
|
/** Single-Shift 3 */
|
||||||
|
export const SS3 = '\x8f';
|
||||||
|
/** Device Control String */
|
||||||
|
export const DCS = '\x90';
|
||||||
|
/** Private Use 1 */
|
||||||
|
export const PU1 = '\x91';
|
||||||
|
/** Private Use 2 */
|
||||||
|
export const PU2 = '\x92';
|
||||||
|
/** Set Transmit State */
|
||||||
|
export const STS = '\x93';
|
||||||
|
/** Destructive backspace, intended to eliminate ambiguity about meaning of BS. */
|
||||||
|
export const CCH = '\x94';
|
||||||
|
/** Message Waiting */
|
||||||
|
export const MW = '\x95';
|
||||||
|
/** Start of Protected Area */
|
||||||
|
export const SPA = '\x96';
|
||||||
|
/** End of Protected Area */
|
||||||
|
export const EPA = '\x97';
|
||||||
|
/** Start of String */
|
||||||
|
export const SOS = '\x98';
|
||||||
|
/** Single Graphic Character Introducer */
|
||||||
|
export const SGCI = '\x99';
|
||||||
|
/** Single Character Introducer */
|
||||||
|
export const SCI = '\x9a';
|
||||||
|
/** Control Sequence Introducer */
|
||||||
|
export const CSI = '\x9b';
|
||||||
|
/** String Terminator */
|
||||||
|
export const ST = '\x9c';
|
||||||
|
/** Operating System Command */
|
||||||
|
export const OSC = '\x9d';
|
||||||
|
/** Privacy Message */
|
||||||
|
export const PM = '\x9e';
|
||||||
|
/** Application Program Command */
|
||||||
|
export const APC = '\x9f';
|
||||||
|
}
|
||||||
|
|||||||
+11
-10
@@ -29,38 +29,39 @@ describe('InputHandler', () => {
|
|||||||
it('should call Terminal.setOption with correct params', () => {
|
it('should call Terminal.setOption with correct params', () => {
|
||||||
let terminal = new MockInputHandlingTerminal();
|
let terminal = new MockInputHandlingTerminal();
|
||||||
let inputHandler = new InputHandler(terminal);
|
let inputHandler = new InputHandler(terminal);
|
||||||
|
const collect = ' ';
|
||||||
|
|
||||||
inputHandler.setCursorStyle([0]);
|
inputHandler.setCursorStyle([0], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'block');
|
assert.equal(terminal.options['cursorStyle'], 'block');
|
||||||
assert.equal(terminal.options['cursorBlink'], true);
|
assert.equal(terminal.options['cursorBlink'], true);
|
||||||
|
|
||||||
terminal.options = {};
|
terminal.options = {};
|
||||||
inputHandler.setCursorStyle([1]);
|
inputHandler.setCursorStyle([1], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'block');
|
assert.equal(terminal.options['cursorStyle'], 'block');
|
||||||
assert.equal(terminal.options['cursorBlink'], true);
|
assert.equal(terminal.options['cursorBlink'], true);
|
||||||
|
|
||||||
terminal.options = {};
|
terminal.options = {};
|
||||||
inputHandler.setCursorStyle([2]);
|
inputHandler.setCursorStyle([2], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'block');
|
assert.equal(terminal.options['cursorStyle'], 'block');
|
||||||
assert.equal(terminal.options['cursorBlink'], false);
|
assert.equal(terminal.options['cursorBlink'], false);
|
||||||
|
|
||||||
terminal.options = {};
|
terminal.options = {};
|
||||||
inputHandler.setCursorStyle([3]);
|
inputHandler.setCursorStyle([3], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'underline');
|
assert.equal(terminal.options['cursorStyle'], 'underline');
|
||||||
assert.equal(terminal.options['cursorBlink'], true);
|
assert.equal(terminal.options['cursorBlink'], true);
|
||||||
|
|
||||||
terminal.options = {};
|
terminal.options = {};
|
||||||
inputHandler.setCursorStyle([4]);
|
inputHandler.setCursorStyle([4], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'underline');
|
assert.equal(terminal.options['cursorStyle'], 'underline');
|
||||||
assert.equal(terminal.options['cursorBlink'], false);
|
assert.equal(terminal.options['cursorBlink'], false);
|
||||||
|
|
||||||
terminal.options = {};
|
terminal.options = {};
|
||||||
inputHandler.setCursorStyle([5]);
|
inputHandler.setCursorStyle([5], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'bar');
|
assert.equal(terminal.options['cursorStyle'], 'bar');
|
||||||
assert.equal(terminal.options['cursorBlink'], true);
|
assert.equal(terminal.options['cursorBlink'], true);
|
||||||
|
|
||||||
terminal.options = {};
|
terminal.options = {};
|
||||||
inputHandler.setCursorStyle([6]);
|
inputHandler.setCursorStyle([6], collect);
|
||||||
assert.equal(terminal.options['cursorStyle'], 'bar');
|
assert.equal(terminal.options['cursorStyle'], 'bar');
|
||||||
assert.equal(terminal.options['cursorBlink'], false);
|
assert.equal(terminal.options['cursorBlink'], false);
|
||||||
});
|
});
|
||||||
@@ -68,14 +69,14 @@ describe('InputHandler', () => {
|
|||||||
describe('setMode', () => {
|
describe('setMode', () => {
|
||||||
it('should toggle Terminal.bracketedPasteMode', () => {
|
it('should toggle Terminal.bracketedPasteMode', () => {
|
||||||
let terminal = new MockInputHandlingTerminal();
|
let terminal = new MockInputHandlingTerminal();
|
||||||
terminal.prefix = '?';
|
const collect = '?';
|
||||||
terminal.bracketedPasteMode = false;
|
terminal.bracketedPasteMode = false;
|
||||||
let inputHandler = new InputHandler(terminal);
|
let inputHandler = new InputHandler(terminal);
|
||||||
// Set bracketed paste mode
|
// Set bracketed paste mode
|
||||||
inputHandler.setMode([2004]);
|
inputHandler.setMode([2004], collect);
|
||||||
assert.equal(terminal.bracketedPasteMode, true);
|
assert.equal(terminal.bracketedPasteMode, true);
|
||||||
// Reset bracketed paste mode
|
// Reset bracketed paste mode
|
||||||
inputHandler.resetMode([2004]);
|
inputHandler.resetMode([2004], collect);
|
||||||
assert.equal(terminal.bracketedPasteMode, false);
|
assert.equal(terminal.bracketedPasteMode, false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+524
-75
@@ -4,12 +4,104 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CharData, IInputHandler, IInputHandlingTerminal } from './Types';
|
import { CharData, IInputHandler, IDcsHandler, IEscapeSequenceParser } from './Types';
|
||||||
import { C0 } from './EscapeSequences';
|
import { C0, C1 } from './EscapeSequences';
|
||||||
import { DEFAULT_CHARSET } from './Charsets';
|
import { CHARSETS, DEFAULT_CHARSET } from './Charsets';
|
||||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer';
|
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from './Buffer';
|
||||||
import { FLAGS } from './renderer/Types';
|
import { FLAGS } from './renderer/Types';
|
||||||
import { wcwidth } from './CharWidth';
|
import { wcwidth } from './CharWidth';
|
||||||
|
import { EscapeSequenceParser } from './EscapeSequenceParser';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map collect to glevel. Used in `selectCharset`.
|
||||||
|
*/
|
||||||
|
const GLEVEL = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS subparser implementations
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS + q Pt ST (xterm)
|
||||||
|
* Request Terminfo String
|
||||||
|
* not supported
|
||||||
|
*/
|
||||||
|
class RequestTerminfo implements IDcsHandler {
|
||||||
|
private _data: string;
|
||||||
|
constructor(private _terminal: any) { }
|
||||||
|
hook(collect: string, params: number[], flag: number): void {
|
||||||
|
this._data = '';
|
||||||
|
}
|
||||||
|
put(data: string, start: number, end: number): void {
|
||||||
|
this._data += data.substring(start, end);
|
||||||
|
}
|
||||||
|
unhook(): void {
|
||||||
|
// invalid: DCS 0 + r Pt ST
|
||||||
|
this._terminal.send(`${C0.ESC}P0+r${this._data}${C0.ESC}\\`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS $ q Pt ST
|
||||||
|
* DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)
|
||||||
|
* Request Status String (DECRQSS), VT420 and up.
|
||||||
|
* Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
|
||||||
|
*/
|
||||||
|
class DECRQSS implements IDcsHandler {
|
||||||
|
private _data: string;
|
||||||
|
|
||||||
|
constructor(private _terminal: any) { }
|
||||||
|
|
||||||
|
hook(collect: string, params: number[], flag: number): void {
|
||||||
|
// reset data
|
||||||
|
this._data = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
put(data: string, start: number, end: number): void {
|
||||||
|
this._data += data.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
unhook(): void {
|
||||||
|
switch (this._data) {
|
||||||
|
// valid: DCS 1 $ r Pt ST (xterm)
|
||||||
|
case '"q': // DECSCA
|
||||||
|
return this._terminal.send(`${C0.ESC}P1$r0"q${C0.ESC}\\`);
|
||||||
|
case '"p': // DECSCL
|
||||||
|
return this._terminal.send(`${C0.ESC}P1$r61"p${C0.ESC}\\`);
|
||||||
|
case 'r': // DECSTBM
|
||||||
|
let pt = '' + (this._terminal.buffer.scrollTop + 1) +
|
||||||
|
';' + (this._terminal.buffer.scrollBottom + 1) + 'r';
|
||||||
|
return this._terminal.send(`${C0.ESC}P1$r${pt}${C0.ESC}\\`);
|
||||||
|
case 'm': // SGR
|
||||||
|
// TODO: report real settings instead of 0m
|
||||||
|
return this._terminal.send(`${C0.ESC}P1$r0m${C0.ESC}\\`);
|
||||||
|
case ' q': // DECSCUSR
|
||||||
|
const STYLES = {'block': 2, 'underline': 4, 'bar': 6};
|
||||||
|
let style = STYLES[this._terminal.getOption('cursorStyle')];
|
||||||
|
style -= this._terminal.getOption('cursorBlink');
|
||||||
|
return this._terminal.send(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
|
||||||
|
default:
|
||||||
|
// invalid: DCS 0 $ r Pt ST (xterm)
|
||||||
|
this._terminal.error('Unknown DCS $q %s', this._data);
|
||||||
|
this._terminal.send(`${C0.ESC}P0$r${this._data}${C0.ESC}\\`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS Ps; Ps| Pt ST
|
||||||
|
* DECUDK (https://vt100.net/docs/vt510-rm/DECUDK.html)
|
||||||
|
* not supported
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS + p Pt ST (xterm)
|
||||||
|
* Set Terminfo Data
|
||||||
|
* not supported
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The terminal's standard implementation of IInputHandler, this handles all
|
* The terminal's standard implementation of IInputHandler, this handles all
|
||||||
@@ -19,24 +111,237 @@ import { wcwidth } from './CharWidth';
|
|||||||
* each function's header comment.
|
* each function's header comment.
|
||||||
*/
|
*/
|
||||||
export class InputHandler implements IInputHandler {
|
export class InputHandler implements IInputHandler {
|
||||||
constructor(private _terminal: IInputHandlingTerminal) { }
|
private _surrogateHigh: string;
|
||||||
|
|
||||||
public addChar(char: string, code: number): void {
|
constructor(
|
||||||
if (char >= ' ') {
|
private _terminal: any, // TODO: reestablish IInputHandlingTerminal here
|
||||||
|
private _parser: IEscapeSequenceParser = new EscapeSequenceParser())
|
||||||
|
{
|
||||||
|
this._surrogateHigh = '';
|
||||||
|
|
||||||
// make buffer local for faster access
|
/**
|
||||||
const buffer = this._terminal.buffer;
|
* custom fallback handlers
|
||||||
|
*/
|
||||||
|
this._parser.setCsiHandlerFallback((collect: string, params: number[], flag: number) => {
|
||||||
|
this._terminal.error('Unknown CSI code: ', collect, params, String.fromCharCode(flag));
|
||||||
|
});
|
||||||
|
this._parser.setEscHandlerFallback((collect: string, flag: number) => {
|
||||||
|
this._terminal.error('Unknown ESC code: ', collect, String.fromCharCode(flag));
|
||||||
|
});
|
||||||
|
this._parser.setExecuteHandlerFallback((code: number) => {
|
||||||
|
this._terminal.error('Unknown EXECUTE code: ', code);
|
||||||
|
});
|
||||||
|
this._parser.setOscHandlerFallback((identifier: number, data: string) => {
|
||||||
|
this._terminal.error('Unknown OSC code: ', identifier, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print handler
|
||||||
|
*/
|
||||||
|
this._parser.setPrintHandler((data, start, end): void => this.print(data, start, end));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSI handler
|
||||||
|
*/
|
||||||
|
this._parser.setCsiHandler('@', (params, collect) => this.insertChars(params));
|
||||||
|
this._parser.setCsiHandler('A', (params, collect) => this.cursorUp(params));
|
||||||
|
this._parser.setCsiHandler('B', (params, collect) => this.cursorDown(params));
|
||||||
|
this._parser.setCsiHandler('C', (params, collect) => this.cursorForward(params));
|
||||||
|
this._parser.setCsiHandler('D', (params, collect) => this.cursorBackward(params));
|
||||||
|
this._parser.setCsiHandler('E', (params, collect) => this.cursorNextLine(params));
|
||||||
|
this._parser.setCsiHandler('F', (params, collect) => this.cursorPrecedingLine(params));
|
||||||
|
this._parser.setCsiHandler('G', (params, collect) => this.cursorCharAbsolute(params));
|
||||||
|
this._parser.setCsiHandler('H', (params, collect) => this.cursorPosition(params));
|
||||||
|
this._parser.setCsiHandler('I', (params, collect) => this.cursorForwardTab(params));
|
||||||
|
this._parser.setCsiHandler('J', (params, collect) => this.eraseInDisplay(params));
|
||||||
|
this._parser.setCsiHandler('K', (params, collect) => this.eraseInLine(params));
|
||||||
|
this._parser.setCsiHandler('L', (params, collect) => this.insertLines(params));
|
||||||
|
this._parser.setCsiHandler('M', (params, collect) => this.deleteLines(params));
|
||||||
|
this._parser.setCsiHandler('P', (params, collect) => this.deleteChars(params));
|
||||||
|
this._parser.setCsiHandler('S', (params, collect) => this.scrollUp(params));
|
||||||
|
this._parser.setCsiHandler('T', (params, collect) => this.scrollDown(params, collect));
|
||||||
|
this._parser.setCsiHandler('X', (params, collect) => this.eraseChars(params));
|
||||||
|
this._parser.setCsiHandler('Z', (params, collect) => this.cursorBackwardTab(params));
|
||||||
|
this._parser.setCsiHandler('`', (params, collect) => this.charPosAbsolute(params));
|
||||||
|
this._parser.setCsiHandler('a', (params, collect) => this.HPositionRelative(params));
|
||||||
|
this._parser.setCsiHandler('b', (params, collect) => this.repeatPrecedingCharacter(params));
|
||||||
|
this._parser.setCsiHandler('c', (params, collect) => this.sendDeviceAttributes(params, collect));
|
||||||
|
this._parser.setCsiHandler('d', (params, collect) => this.linePosAbsolute(params));
|
||||||
|
this._parser.setCsiHandler('e', (params, collect) => this.VPositionRelative(params));
|
||||||
|
this._parser.setCsiHandler('f', (params, collect) => this.HVPosition(params));
|
||||||
|
this._parser.setCsiHandler('g', (params, collect) => this.tabClear(params));
|
||||||
|
this._parser.setCsiHandler('h', (params, collect) => this.setMode(params, collect));
|
||||||
|
this._parser.setCsiHandler('l', (params, collect) => this.resetMode(params, collect));
|
||||||
|
this._parser.setCsiHandler('m', (params, collect) => this.charAttributes(params));
|
||||||
|
this._parser.setCsiHandler('n', (params, collect) => this.deviceStatus(params, collect));
|
||||||
|
this._parser.setCsiHandler('p', (params, collect) => this.softReset(params, collect));
|
||||||
|
this._parser.setCsiHandler('q', (params, collect) => this.setCursorStyle(params, collect));
|
||||||
|
this._parser.setCsiHandler('r', (params, collect) => this.setScrollRegion(params, collect));
|
||||||
|
this._parser.setCsiHandler('s', (params, collect) => this.saveCursor(params));
|
||||||
|
this._parser.setCsiHandler('u', (params, collect) => this.restoreCursor(params));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* execute handler
|
||||||
|
*/
|
||||||
|
this._parser.setExecuteHandler(C0.BEL, () => this.bell());
|
||||||
|
this._parser.setExecuteHandler(C0.LF, () => this.lineFeed());
|
||||||
|
this._parser.setExecuteHandler(C0.VT, () => this.lineFeed());
|
||||||
|
this._parser.setExecuteHandler(C0.FF, () => this.lineFeed());
|
||||||
|
this._parser.setExecuteHandler(C0.CR, () => this.carriageReturn());
|
||||||
|
this._parser.setExecuteHandler(C0.BS, () => this.backspace());
|
||||||
|
this._parser.setExecuteHandler(C0.HT, () => this.tab());
|
||||||
|
this._parser.setExecuteHandler(C0.SO, () => this.shiftOut());
|
||||||
|
this._parser.setExecuteHandler(C0.SI, () => this.shiftIn());
|
||||||
|
// FIXME: What do to with missing? Old code just added those to print.
|
||||||
|
|
||||||
|
// some C1 control codes - FIXME: should those be enabled by default?
|
||||||
|
this._parser.setExecuteHandler(C1.IND, () => this.index());
|
||||||
|
this._parser.setExecuteHandler(C1.NEL, () => this.nextLine());
|
||||||
|
this._parser.setExecuteHandler(C1.HTS, () => this.tabSet());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OSC handler
|
||||||
|
*/
|
||||||
|
// 0 - icon name + title
|
||||||
|
this._parser.setOscHandler(0, (data) => this.setTitle(data));
|
||||||
|
// 1 - icon name
|
||||||
|
// 2 - title
|
||||||
|
this._parser.setOscHandler(2, (data) => this.setTitle(data));
|
||||||
|
// 3 - set property X in the form "prop=value"
|
||||||
|
// 4 - Change Color Number
|
||||||
|
// 5 - Change Special Color Number
|
||||||
|
// 6 - Enable/disable Special Color Number c
|
||||||
|
// 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939)
|
||||||
|
// 10 - Change VT100 text foreground color to Pt.
|
||||||
|
// 11 - Change VT100 text background color to Pt.
|
||||||
|
// 12 - Change text cursor color to Pt.
|
||||||
|
// 13 - Change mouse foreground color to Pt.
|
||||||
|
// 14 - Change mouse background color to Pt.
|
||||||
|
// 15 - Change Tektronix foreground color to Pt.
|
||||||
|
// 16 - Change Tektronix background color to Pt.
|
||||||
|
// 17 - Change highlight background color to Pt.
|
||||||
|
// 18 - Change Tektronix cursor color to Pt.
|
||||||
|
// 19 - Change highlight foreground color to Pt.
|
||||||
|
// 46 - Change Log File to Pt.
|
||||||
|
// 50 - Set Font to Pt.
|
||||||
|
// 51 - reserved for Emacs shell.
|
||||||
|
// 52 - Manipulate Selection Data.
|
||||||
|
// 104 ; c - Reset Color Number c.
|
||||||
|
// 105 ; c - Reset Special Color Number c.
|
||||||
|
// 106 ; c; f - Enable/disable Special Color Number c.
|
||||||
|
// 110 - Reset VT100 text foreground color.
|
||||||
|
// 111 - Reset VT100 text background color.
|
||||||
|
// 112 - Reset text cursor color.
|
||||||
|
// 113 - Reset mouse foreground color.
|
||||||
|
// 114 - Reset mouse background color.
|
||||||
|
// 115 - Reset Tektronix foreground color.
|
||||||
|
// 116 - Reset Tektronix background color.
|
||||||
|
// 117 - Reset highlight color.
|
||||||
|
// 118 - Reset Tektronix cursor color.
|
||||||
|
// 119 - Reset highlight foreground color.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC handlers
|
||||||
|
*/
|
||||||
|
this._parser.setEscHandler('7', () => this.saveCursor([]));
|
||||||
|
this._parser.setEscHandler('8', () => this.restoreCursor([]));
|
||||||
|
this._parser.setEscHandler('D', () => this.index());
|
||||||
|
this._parser.setEscHandler('E', () => this.nextLine());
|
||||||
|
this._parser.setEscHandler('H', () => this.tabSet());
|
||||||
|
this._parser.setEscHandler('M', () => this.reverseIndex());
|
||||||
|
this._parser.setEscHandler('=', () => this.keypadApplicationMode());
|
||||||
|
this._parser.setEscHandler('>', () => this.keypadNumericMode());
|
||||||
|
this._parser.setEscHandler('c', () => this.reset());
|
||||||
|
this._parser.setEscHandler('n', () => this.setgLevel(2));
|
||||||
|
this._parser.setEscHandler('o', () => this.setgLevel(3));
|
||||||
|
this._parser.setEscHandler('|', () => this.setgLevel(3));
|
||||||
|
this._parser.setEscHandler('}', () => this.setgLevel(2));
|
||||||
|
this._parser.setEscHandler('~', () => this.setgLevel(1));
|
||||||
|
this._parser.setEscHandler('%@', () => this.selectDefaultCharset());
|
||||||
|
this._parser.setEscHandler('%G', () => this.selectDefaultCharset());
|
||||||
|
for (let flag in CHARSETS) {
|
||||||
|
this._parser.setEscHandler('(' + flag, () => this.selectCharset('(' + flag));
|
||||||
|
this._parser.setEscHandler(')' + flag, () => this.selectCharset(')' + flag));
|
||||||
|
this._parser.setEscHandler('*' + flag, () => this.selectCharset('*' + flag));
|
||||||
|
this._parser.setEscHandler('+' + flag, () => this.selectCharset('+' + flag));
|
||||||
|
this._parser.setEscHandler('-' + flag, () => this.selectCharset('-' + flag));
|
||||||
|
this._parser.setEscHandler('.' + flag, () => this.selectCharset('.' + flag));
|
||||||
|
this._parser.setEscHandler('/' + flag, () => this.selectCharset('/' + flag)); // TODO: supported?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* error handler
|
||||||
|
*/
|
||||||
|
this._parser.setErrorHandler((state) => {
|
||||||
|
this._terminal.error('Parsing error: ', state);
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS handler
|
||||||
|
*/
|
||||||
|
this._parser.setDcsHandler('$q', new DECRQSS(this._terminal));
|
||||||
|
this._parser.setDcsHandler('+q', new RequestTerminfo(this._terminal));
|
||||||
|
}
|
||||||
|
|
||||||
|
public parse(data: string): void {
|
||||||
|
let buffer = this._terminal.buffer;
|
||||||
|
const cursorStartX = buffer.x;
|
||||||
|
const cursorStartY = buffer.y;
|
||||||
|
if (this._terminal.debug) {
|
||||||
|
this._terminal.log('data: ' + data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply leftover surrogate high from last write
|
||||||
|
if (this._surrogateHigh) {
|
||||||
|
data = this._surrogateHigh + data;
|
||||||
|
this._surrogateHigh = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this._parser.parse(data);
|
||||||
|
|
||||||
|
buffer = this._terminal.buffer;
|
||||||
|
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
||||||
|
this._terminal.emit('cursormove');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public print(data: string, start: number, end: number): void {
|
||||||
|
let ch;
|
||||||
|
let code;
|
||||||
|
let low;
|
||||||
|
let chWidth;
|
||||||
|
const buffer = this._terminal.buffer;
|
||||||
|
for (let i = start; i < end; ++i) {
|
||||||
|
ch = data.charAt(i);
|
||||||
|
code = data.charCodeAt(i);
|
||||||
|
if (0xD800 <= code && code <= 0xDBFF) {
|
||||||
|
// we got a surrogate high
|
||||||
|
// get surrogate low (next 2 bytes)
|
||||||
|
low = data.charCodeAt(i + 1);
|
||||||
|
if (isNaN(low)) {
|
||||||
|
// end of data stream, save surrogate high
|
||||||
|
this._surrogateHigh = ch;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
|
||||||
|
ch += data.charAt(i + 1);
|
||||||
|
}
|
||||||
|
// surrogate low - already handled above
|
||||||
|
if (0xDC00 <= code && code <= 0xDFFF) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// calculate print space
|
// calculate print space
|
||||||
// expensive call, therefore we save width in line buffer
|
// expensive call, therefore we save width in line buffer
|
||||||
const chWidth = wcwidth(code);
|
chWidth = wcwidth(code);
|
||||||
|
|
||||||
if (this._terminal.charset && this._terminal.charset[char]) {
|
if (this._terminal.charset && this._terminal.charset[ch]) {
|
||||||
char = this._terminal.charset[char];
|
ch = this._terminal.charset[ch];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._terminal.options.screenReaderMode) {
|
if (this._terminal.options.screenReaderMode) {
|
||||||
this._terminal.emit('a11y.char', char);
|
this._terminal.emit('a11y.char', ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
let row = buffer.y + buffer.ybase;
|
let row = buffer.y + buffer.ybase;
|
||||||
@@ -49,16 +354,16 @@ export class InputHandler implements IInputHandler {
|
|||||||
if (!buffer.lines.get(row)[buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) {
|
if (!buffer.lines.get(row)[buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) {
|
||||||
// found empty cell after fullwidth, need to go 2 cells back
|
// found empty cell after fullwidth, need to go 2 cells back
|
||||||
if (buffer.lines.get(row)[buffer.x - 2]) {
|
if (buffer.lines.get(row)[buffer.x - 2]) {
|
||||||
buffer.lines.get(row)[buffer.x - 2][CHAR_DATA_CHAR_INDEX] += char;
|
buffer.lines.get(row)[buffer.x - 2][CHAR_DATA_CHAR_INDEX] += ch;
|
||||||
buffer.lines.get(row)[buffer.x - 2][3] = char.charCodeAt(0);
|
buffer.lines.get(row)[buffer.x - 2][3] = ch.charCodeAt(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buffer.lines.get(row)[buffer.x - 1][CHAR_DATA_CHAR_INDEX] += char;
|
buffer.lines.get(row)[buffer.x - 1][CHAR_DATA_CHAR_INDEX] += ch;
|
||||||
buffer.lines.get(row)[buffer.x - 1][3] = char.charCodeAt(0);
|
buffer.lines.get(row)[buffer.x - 1][3] = ch.charCodeAt(0);
|
||||||
}
|
}
|
||||||
this._terminal.updateRange(buffer.y);
|
this._terminal.updateRange(buffer.y);
|
||||||
}
|
}
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// goto next line if ch would overflow
|
// goto next line if ch would overflow
|
||||||
@@ -78,7 +383,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (chWidth === 2) { // FIXME: check for xterm behavior
|
if (chWidth === 2) { // FIXME: check for xterm behavior
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +407,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.lines.get(row)[buffer.x] = [this._terminal.curAttr, char, chWidth, char.charCodeAt(0)];
|
buffer.lines.get(row)[buffer.x] = [this._terminal.curAttr, ch, chWidth, ch.charCodeAt(0)];
|
||||||
buffer.x++;
|
buffer.x++;
|
||||||
this._terminal.updateRange(buffer.y);
|
this._terminal.updateRange(buffer.y);
|
||||||
|
|
||||||
@@ -551,19 +856,21 @@ export class InputHandler implements IInputHandler {
|
|||||||
/**
|
/**
|
||||||
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
||||||
*/
|
*/
|
||||||
public scrollDown(params: number[]): void {
|
public scrollDown(params: number[], collect?: string): void {
|
||||||
let param = params[0] || 1;
|
if (params.length < 2 && !collect) {
|
||||||
|
let param = params[0] || 1;
|
||||||
|
|
||||||
// make buffer local for faster access
|
// make buffer local for faster access
|
||||||
const buffer = this._terminal.buffer;
|
const buffer = this._terminal.buffer;
|
||||||
|
|
||||||
while (param--) {
|
while (param--) {
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, this._terminal.blankLine());
|
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, this._terminal.blankLine());
|
||||||
|
}
|
||||||
|
// this.maxRange();
|
||||||
|
this._terminal.updateRange(buffer.scrollTop);
|
||||||
|
this._terminal.updateRange(buffer.scrollBottom);
|
||||||
}
|
}
|
||||||
// this.maxRange();
|
|
||||||
this._terminal.updateRange(buffer.scrollTop);
|
|
||||||
this._terminal.updateRange(buffer.scrollBottom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -687,18 +994,18 @@ export class InputHandler implements IInputHandler {
|
|||||||
* xterm/charproc.c - line 2012, for more information.
|
* xterm/charproc.c - line 2012, for more information.
|
||||||
* vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
|
* vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
|
||||||
*/
|
*/
|
||||||
public sendDeviceAttributes(params: number[]): void {
|
public sendDeviceAttributes(params: number[], collect?: string): void {
|
||||||
if (params[0] > 0) {
|
if (params[0] > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._terminal.prefix) {
|
if (!collect) {
|
||||||
if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {
|
if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {
|
||||||
this._terminal.send(C0.ESC + '[?1;2c');
|
this._terminal.send(C0.ESC + '[?1;2c');
|
||||||
} else if (this._terminal.is('linux')) {
|
} else if (this._terminal.is('linux')) {
|
||||||
this._terminal.send(C0.ESC + '[?6c');
|
this._terminal.send(C0.ESC + '[?6c');
|
||||||
}
|
}
|
||||||
} else if (this._terminal.prefix === '>') {
|
} else if (collect === '>') {
|
||||||
// xterm and urxvt
|
// xterm and urxvt
|
||||||
// seem to spit this
|
// seem to spit this
|
||||||
// out around ~370 times (?).
|
// out around ~370 times (?).
|
||||||
@@ -874,7 +1181,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
* Modes:
|
* Modes:
|
||||||
* http: *vt100.net/docs/vt220-rm/chapter4.html
|
* http: *vt100.net/docs/vt220-rm/chapter4.html
|
||||||
*/
|
*/
|
||||||
public setMode(params: number[]): void {
|
public setMode(params: number[], collect?: string): void {
|
||||||
if (params.length > 1) {
|
if (params.length > 1) {
|
||||||
for (let i = 0; i < params.length; i++) {
|
for (let i = 0; i < params.length; i++) {
|
||||||
this.setMode([params[i]]);
|
this.setMode([params[i]]);
|
||||||
@@ -883,7 +1190,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._terminal.prefix) {
|
if (!collect) {
|
||||||
switch (params[0]) {
|
switch (params[0]) {
|
||||||
case 4:
|
case 4:
|
||||||
this._terminal.insertMode = true;
|
this._terminal.insertMode = true;
|
||||||
@@ -892,7 +1199,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
// this._t.convertEol = true;
|
// this._t.convertEol = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (this._terminal.prefix === '?') {
|
} else if (collect === '?') {
|
||||||
switch (params[0]) {
|
switch (params[0]) {
|
||||||
case 1:
|
case 1:
|
||||||
this._terminal.applicationCursor = true;
|
this._terminal.applicationCursor = true;
|
||||||
@@ -1068,7 +1375,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
* Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
|
* Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
|
||||||
* Ps = 2 0 0 4 -> Reset bracketed paste mode.
|
* Ps = 2 0 0 4 -> Reset bracketed paste mode.
|
||||||
*/
|
*/
|
||||||
public resetMode(params: number[]): void {
|
public resetMode(params: number[], collect?: string): void {
|
||||||
if (params.length > 1) {
|
if (params.length > 1) {
|
||||||
for (let i = 0; i < params.length; i++) {
|
for (let i = 0; i < params.length; i++) {
|
||||||
this.resetMode([params[i]]);
|
this.resetMode([params[i]]);
|
||||||
@@ -1077,7 +1384,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._terminal.prefix) {
|
if (!collect) {
|
||||||
switch (params[0]) {
|
switch (params[0]) {
|
||||||
case 4:
|
case 4:
|
||||||
this._terminal.insertMode = false;
|
this._terminal.insertMode = false;
|
||||||
@@ -1086,7 +1393,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
// this._t.convertEol = false;
|
// this._t.convertEol = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (this._terminal.prefix === '?') {
|
} else if (collect === '?') {
|
||||||
switch (params[0]) {
|
switch (params[0]) {
|
||||||
case 1:
|
case 1:
|
||||||
this._terminal.applicationCursor = false;
|
this._terminal.applicationCursor = false;
|
||||||
@@ -1369,8 +1676,8 @@ export class InputHandler implements IInputHandler {
|
|||||||
* CSI ? 5 3 n Locator available, if compiled-in, or
|
* CSI ? 5 3 n Locator available, if compiled-in, or
|
||||||
* CSI ? 5 0 n No Locator, if not.
|
* CSI ? 5 0 n No Locator, if not.
|
||||||
*/
|
*/
|
||||||
public deviceStatus(params: number[]): void {
|
public deviceStatus(params: number[], collect?: string): void {
|
||||||
if (!this._terminal.prefix) {
|
if (!collect) {
|
||||||
switch (params[0]) {
|
switch (params[0]) {
|
||||||
case 5:
|
case 5:
|
||||||
// status report
|
// status report
|
||||||
@@ -1385,7 +1692,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
+ 'R');
|
+ 'R');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (this._terminal.prefix === '?') {
|
} else if (collect === '?') {
|
||||||
// modern xterm doesnt seem to
|
// modern xterm doesnt seem to
|
||||||
// respond to any of these except ?6, 6, and 5
|
// respond to any of these except ?6, 6, and 5
|
||||||
switch (params[0]) {
|
switch (params[0]) {
|
||||||
@@ -1421,21 +1728,23 @@ export class InputHandler implements IInputHandler {
|
|||||||
* CSI ! p Soft terminal reset (DECSTR).
|
* CSI ! p Soft terminal reset (DECSTR).
|
||||||
* http://vt100.net/docs/vt220-rm/table4-10.html
|
* http://vt100.net/docs/vt220-rm/table4-10.html
|
||||||
*/
|
*/
|
||||||
public softReset(params: number[]): void {
|
public softReset(params: number[], collect?: string): void {
|
||||||
this._terminal.cursorHidden = false;
|
if (collect === '!') {
|
||||||
this._terminal.insertMode = false;
|
this._terminal.cursorHidden = false;
|
||||||
this._terminal.originMode = false;
|
this._terminal.insertMode = false;
|
||||||
this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false
|
this._terminal.originMode = false;
|
||||||
this._terminal.applicationKeypad = false; // ?
|
this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false
|
||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.applicationKeypad = false; // ?
|
||||||
this._terminal.applicationCursor = false;
|
this._terminal.viewport.syncScrollArea();
|
||||||
this._terminal.buffer.scrollTop = 0;
|
this._terminal.applicationCursor = false;
|
||||||
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
|
this._terminal.buffer.scrollTop = 0;
|
||||||
this._terminal.curAttr = this._terminal.defAttr;
|
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
|
||||||
this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?
|
this._terminal.curAttr = this._terminal.defAttr;
|
||||||
this._terminal.charset = null;
|
this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?
|
||||||
this._terminal.glevel = 0; // ??
|
this._terminal.charset = null;
|
||||||
this._terminal.charsets = [null]; // ??
|
this._terminal.glevel = 0; // ??
|
||||||
|
this._terminal.charsets = [null]; // ??
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1448,24 +1757,26 @@ export class InputHandler implements IInputHandler {
|
|||||||
* Ps = 5 -> blinking bar (xterm).
|
* Ps = 5 -> blinking bar (xterm).
|
||||||
* Ps = 6 -> steady bar (xterm).
|
* Ps = 6 -> steady bar (xterm).
|
||||||
*/
|
*/
|
||||||
public setCursorStyle(params?: number[]): void {
|
public setCursorStyle(params?: number[], collect?: string): void {
|
||||||
const param = params[0] < 1 ? 1 : params[0];
|
if (collect === ' ') {
|
||||||
switch (param) {
|
const param = params[0] < 1 ? 1 : params[0];
|
||||||
case 1:
|
switch (param) {
|
||||||
case 2:
|
case 1:
|
||||||
this._terminal.setOption('cursorStyle', 'block');
|
case 2:
|
||||||
break;
|
this._terminal.setOption('cursorStyle', 'block');
|
||||||
case 3:
|
break;
|
||||||
case 4:
|
case 3:
|
||||||
this._terminal.setOption('cursorStyle', 'underline');
|
case 4:
|
||||||
break;
|
this._terminal.setOption('cursorStyle', 'underline');
|
||||||
case 5:
|
break;
|
||||||
case 6:
|
case 5:
|
||||||
this._terminal.setOption('cursorStyle', 'bar');
|
case 6:
|
||||||
break;
|
this._terminal.setOption('cursorStyle', 'bar');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const isBlinking = param % 2 === 1;
|
||||||
|
this._terminal.setOption('cursorBlink', isBlinking);
|
||||||
}
|
}
|
||||||
const isBlinking = param % 2 === 1;
|
|
||||||
this._terminal.setOption('cursorBlink', isBlinking);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1474,8 +1785,8 @@ export class InputHandler implements IInputHandler {
|
|||||||
* dow) (DECSTBM).
|
* dow) (DECSTBM).
|
||||||
* CSI ? Pm r
|
* CSI ? Pm r
|
||||||
*/
|
*/
|
||||||
public setScrollRegion(params: number[]): void {
|
public setScrollRegion(params: number[], collect?: string): void {
|
||||||
if (this._terminal.prefix) return;
|
if (collect) return;
|
||||||
this._terminal.buffer.scrollTop = (params[0] || 1) - 1;
|
this._terminal.buffer.scrollTop = (params[0] || 1) - 1;
|
||||||
this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1;
|
this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1;
|
||||||
this._terminal.buffer.x = 0;
|
this._terminal.buffer.x = 0;
|
||||||
@@ -1485,6 +1796,7 @@ export class InputHandler implements IInputHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* CSI s
|
* CSI s
|
||||||
|
* ESC 7
|
||||||
* Save cursor (ANSI.SYS).
|
* Save cursor (ANSI.SYS).
|
||||||
*/
|
*/
|
||||||
public saveCursor(params: number[]): void {
|
public saveCursor(params: number[]): void {
|
||||||
@@ -1495,10 +1807,147 @@ export class InputHandler implements IInputHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* CSI u
|
* CSI u
|
||||||
|
* ESC 8
|
||||||
* Restore cursor (ANSI.SYS).
|
* Restore cursor (ANSI.SYS).
|
||||||
*/
|
*/
|
||||||
public restoreCursor(params: number[]): void {
|
public restoreCursor(params: number[]): void {
|
||||||
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
|
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
|
||||||
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
|
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OSC 0; <data> ST (set icon name + window title)
|
||||||
|
* OSC 2; <data> ST (set window title)
|
||||||
|
* Proxy to set window title. Icon name is not supported.
|
||||||
|
*/
|
||||||
|
public setTitle(data: string): void {
|
||||||
|
this._terminal.handleTitle(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC E
|
||||||
|
* C1.NEL
|
||||||
|
* DEC mnemonic: NEL (https://vt100.net/docs/vt510-rm/NEL)
|
||||||
|
* Moves cursor to first position on next line.
|
||||||
|
*/
|
||||||
|
public nextLine(): void {
|
||||||
|
this._terminal.buffer.x = 0;
|
||||||
|
this.index();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC =
|
||||||
|
* DEC mnemonic: DECKPAM (https://vt100.net/docs/vt510-rm/DECKPAM.html)
|
||||||
|
* Enables the numeric keypad to send application sequences to the host.
|
||||||
|
*/
|
||||||
|
public keypadApplicationMode(): void {
|
||||||
|
this._terminal.log('Serial port requested application keypad.');
|
||||||
|
this._terminal.applicationKeypad = true;
|
||||||
|
if (this._terminal.viewport) {
|
||||||
|
this._terminal.viewport.syncScrollArea();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC >
|
||||||
|
* DEC mnemonic: DECKPNM (https://vt100.net/docs/vt510-rm/DECKPNM.html)
|
||||||
|
* Enables the keypad to send numeric characters to the host.
|
||||||
|
*/
|
||||||
|
public keypadNumericMode(): void {
|
||||||
|
this._terminal.log('Switching back to normal keypad.');
|
||||||
|
this._terminal.applicationKeypad = false;
|
||||||
|
if (this._terminal.viewport) {
|
||||||
|
this._terminal.viewport.syncScrollArea();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC % @
|
||||||
|
* ESC % G
|
||||||
|
* Select default character set. UTF-8 is not supported (string are unicode anyways)
|
||||||
|
* therefore ESC % G does the same.
|
||||||
|
*/
|
||||||
|
public selectDefaultCharset(): void {
|
||||||
|
this._terminal.setgLevel(0);
|
||||||
|
this._terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC ( C
|
||||||
|
* Designate G0 Character Set, VT100, ISO 2022.
|
||||||
|
* ESC ) C
|
||||||
|
* Designate G1 Character Set (ISO 2022, VT100).
|
||||||
|
* ESC * C
|
||||||
|
* Designate G2 Character Set (ISO 2022, VT220).
|
||||||
|
* ESC + C
|
||||||
|
* Designate G3 Character Set (ISO 2022, VT220).
|
||||||
|
* ESC - C
|
||||||
|
* Designate G1 Character Set (VT300).
|
||||||
|
* ESC . C
|
||||||
|
* Designate G2 Character Set (VT300).
|
||||||
|
* ESC / C
|
||||||
|
* Designate G3 Character Set (VT300). C = A -> ISO Latin-1 Supplemental. - Supported?
|
||||||
|
*/
|
||||||
|
public selectCharset(collectAndFlag: string): void {
|
||||||
|
if (collectAndFlag.length !== 2) return this.selectDefaultCharset();
|
||||||
|
if (collectAndFlag[0] === '/') return; // TODO: Is this supported?
|
||||||
|
this._terminal.setgCharset(GLEVEL[collectAndFlag[0]], CHARSETS[collectAndFlag[1]] || DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC D
|
||||||
|
* C1.IND
|
||||||
|
* DEC mnemonic: IND (https://vt100.net/docs/vt510-rm/IND.html)
|
||||||
|
* Moves the cursor down one line in the same column.
|
||||||
|
*/
|
||||||
|
public index(): void {
|
||||||
|
this._terminal.index(); // TODO: save to move from terminal?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC H
|
||||||
|
* C1.HTS
|
||||||
|
* DEC mnemonic: HTS (https://vt100.net/docs/vt510-rm/HTS.html)
|
||||||
|
* Sets a horizontal tab stop at the column position indicated by
|
||||||
|
* the value of the active column when the terminal receives an HTS.
|
||||||
|
*/
|
||||||
|
public tabSet(): void {
|
||||||
|
this._terminal.tabSet(); // TODO: save to move from terminal?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC M
|
||||||
|
* C1.RI
|
||||||
|
* DEC mnemonic: HTS
|
||||||
|
* Moves the cursor up one line in the same column. If the cursor is at the top margin,
|
||||||
|
* the page scrolls down.
|
||||||
|
*/
|
||||||
|
public reverseIndex(): void {
|
||||||
|
this._terminal.reverseIndex(); // TODO: save to move from terminal?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC c
|
||||||
|
* DEC mnemonic: RIS (https://vt100.net/docs/vt510-rm/RIS.html)
|
||||||
|
* Reset to initial state.
|
||||||
|
*/
|
||||||
|
public reset(): void {
|
||||||
|
this._parser.reset();
|
||||||
|
this._terminal.reset(); // TODO: save to move from terminal?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ESC n
|
||||||
|
* ESC o
|
||||||
|
* ESC |
|
||||||
|
* ESC }
|
||||||
|
* ESC ~
|
||||||
|
* DEC mnemonic: LS (https://vt100.net/docs/vt510-rm/LS.html)
|
||||||
|
* When you use a locking shift, the character set remains in GL or GR until
|
||||||
|
* you use another locking shift. (partly supported)
|
||||||
|
*/
|
||||||
|
public setgLevel(level: number): void {
|
||||||
|
this._terminal.setgLevel(level); // TODO: save to move from terminal?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -232,7 +232,7 @@ export class Parser {
|
|||||||
if (ch in normalStateHandler) {
|
if (ch in normalStateHandler) {
|
||||||
normalStateHandler[ch](this, this._inputHandler);
|
normalStateHandler[ch](this, this._inputHandler);
|
||||||
} else {
|
} else {
|
||||||
this._inputHandler.addChar(ch, code);
|
// this._inputHandler.addChar(ch, code);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ParserState.ESCAPED:
|
case ParserState.ESCAPED:
|
||||||
|
|||||||
+3
-9
@@ -32,7 +32,7 @@ import { Viewport } from './Viewport';
|
|||||||
import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard';
|
import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard';
|
||||||
import { C0 } from './EscapeSequences';
|
import { C0 } from './EscapeSequences';
|
||||||
import { InputHandler } from './InputHandler';
|
import { InputHandler } from './InputHandler';
|
||||||
import { Parser } from './Parser';
|
// import { Parser } from './Parser';
|
||||||
import { Renderer } from './renderer/Renderer';
|
import { Renderer } from './renderer/Renderer';
|
||||||
import { Linkifier } from './Linkifier';
|
import { Linkifier } from './Linkifier';
|
||||||
import { SelectionManager } from './SelectionManager';
|
import { SelectionManager } from './SelectionManager';
|
||||||
@@ -195,8 +195,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
|
|
||||||
public params: (string | number)[];
|
public params: (string | number)[];
|
||||||
public currentParam: string | number;
|
public currentParam: string | number;
|
||||||
public prefix: string;
|
|
||||||
public postfix: string;
|
|
||||||
|
|
||||||
// user input states
|
// user input states
|
||||||
public writeBuffer: string[];
|
public writeBuffer: string[];
|
||||||
@@ -218,7 +216,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
|
|
||||||
private _inputHandler: InputHandler;
|
private _inputHandler: InputHandler;
|
||||||
public soundManager: SoundManager;
|
public soundManager: SoundManager;
|
||||||
private _parser: Parser;
|
|
||||||
public renderer: IRenderer;
|
public renderer: IRenderer;
|
||||||
public selectionManager: SelectionManager;
|
public selectionManager: SelectionManager;
|
||||||
public linkifier: ILinkifier;
|
public linkifier: ILinkifier;
|
||||||
@@ -321,8 +318,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
|
|
||||||
this.params = [];
|
this.params = [];
|
||||||
this.currentParam = 0;
|
this.currentParam = 0;
|
||||||
this.prefix = '';
|
|
||||||
this.postfix = '';
|
|
||||||
|
|
||||||
// user input states
|
// user input states
|
||||||
this.writeBuffer = [];
|
this.writeBuffer = [];
|
||||||
@@ -333,7 +328,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
this._userScrolling = false;
|
this._userScrolling = false;
|
||||||
|
|
||||||
this._inputHandler = new InputHandler(this);
|
this._inputHandler = new InputHandler(this);
|
||||||
this._parser = new Parser(this._inputHandler, this);
|
|
||||||
// Reuse renderer if the Terminal is being recreated via a reset call.
|
// Reuse renderer if the Terminal is being recreated via a reset call.
|
||||||
this.renderer = this.renderer || null;
|
this.renderer = this.renderer || null;
|
||||||
this.selectionManager = this.selectionManager || null;
|
this.selectionManager = this.selectionManager || null;
|
||||||
@@ -1318,8 +1312,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
// middle of parsing escape sequence in two chunks. For some reason the
|
// middle of parsing escape sequence in two chunks. For some reason the
|
||||||
// state of the parser resets to 0 after exiting parser.parse. This change
|
// state of the parser resets to 0 after exiting parser.parse. This change
|
||||||
// just sets the state back based on the correct return statement.
|
// just sets the state back based on the correct return statement.
|
||||||
const state = this._parser.parse(data);
|
|
||||||
this._parser.setState(state);
|
this._inputHandler.parse(data);
|
||||||
|
|
||||||
this.updateRange(this.buffer.y);
|
this.updateRange(this.buffer.y);
|
||||||
this.refresh(this._refreshStart, this._refreshEnd);
|
this.refresh(this._refreshStart, this._refreshEnd);
|
||||||
|
|||||||
+166
-12
@@ -45,7 +45,6 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
|||||||
bracketedPasteMode: boolean;
|
bracketedPasteMode: boolean;
|
||||||
defAttr: number;
|
defAttr: number;
|
||||||
curAttr: number;
|
curAttr: number;
|
||||||
prefix: string;
|
|
||||||
savedCols: number;
|
savedCols: number;
|
||||||
x10Mouse: boolean;
|
x10Mouse: boolean;
|
||||||
vt200Mouse: boolean;
|
vt200Mouse: boolean;
|
||||||
@@ -106,10 +105,11 @@ export interface ICompositionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles actions generated by the parser.
|
* Calls the parser and handles actions generated by the parser.
|
||||||
*/
|
*/
|
||||||
export interface IInputHandler {
|
export interface IInputHandler {
|
||||||
addChar(char: string, code: number): void;
|
parse(data: string): void;
|
||||||
|
print(data: string, start: number, end: number): void;
|
||||||
|
|
||||||
/** C0 BEL */ bell(): void;
|
/** C0 BEL */ bell(): void;
|
||||||
/** C0 LF */ lineFeed(): void;
|
/** C0 LF */ lineFeed(): void;
|
||||||
@@ -135,26 +135,49 @@ export interface IInputHandler {
|
|||||||
/** CSI M */ deleteLines(params?: number[]): void;
|
/** CSI M */ deleteLines(params?: number[]): void;
|
||||||
/** CSI P */ deleteChars(params?: number[]): void;
|
/** CSI P */ deleteChars(params?: number[]): void;
|
||||||
/** CSI S */ scrollUp(params?: number[]): void;
|
/** CSI S */ scrollUp(params?: number[]): void;
|
||||||
/** CSI T */ scrollDown(params?: number[]): void;
|
/** CSI T */ scrollDown(params?: number[], collect?: string): void;
|
||||||
/** CSI X */ eraseChars(params?: number[]): void;
|
/** CSI X */ eraseChars(params?: number[]): void;
|
||||||
/** CSI Z */ cursorBackwardTab(params?: number[]): void;
|
/** CSI Z */ cursorBackwardTab(params?: number[]): void;
|
||||||
/** CSI ` */ charPosAbsolute(params?: number[]): void;
|
/** CSI ` */ charPosAbsolute(params?: number[]): void;
|
||||||
/** CSI a */ HPositionRelative(params?: number[]): void;
|
/** CSI a */ HPositionRelative(params?: number[]): void;
|
||||||
/** CSI b */ repeatPrecedingCharacter(params?: number[]): void;
|
/** CSI b */ repeatPrecedingCharacter(params?: number[]): void;
|
||||||
/** CSI c */ sendDeviceAttributes(params?: number[]): void;
|
/** CSI c */ sendDeviceAttributes(params?: number[], collect?: string): void;
|
||||||
/** CSI d */ linePosAbsolute(params?: number[]): void;
|
/** CSI d */ linePosAbsolute(params?: number[]): void;
|
||||||
/** CSI e */ VPositionRelative(params?: number[]): void;
|
/** CSI e */ VPositionRelative(params?: number[]): void;
|
||||||
/** CSI f */ HVPosition(params?: number[]): void;
|
/** CSI f */ HVPosition(params?: number[]): void;
|
||||||
/** CSI g */ tabClear(params?: number[]): void;
|
/** CSI g */ tabClear(params?: number[]): void;
|
||||||
/** CSI h */ setMode(params?: number[]): void;
|
/** CSI h */ setMode(params?: number[], collect?: string): void;
|
||||||
/** CSI l */ resetMode(params?: number[]): void;
|
/** CSI l */ resetMode(params?: number[], collect?: string): void;
|
||||||
/** CSI m */ charAttributes(params?: number[]): void;
|
/** CSI m */ charAttributes(params?: number[]): void;
|
||||||
/** CSI n */ deviceStatus(params?: number[]): void;
|
/** CSI n */ deviceStatus(params?: number[], collect?: string): void;
|
||||||
/** CSI p */ softReset(params?: number[]): void;
|
/** CSI p */ softReset(params?: number[], collect?: string): void;
|
||||||
/** CSI q */ setCursorStyle(params?: number[]): void;
|
/** CSI q */ setCursorStyle(params?: number[], collect?: string): void;
|
||||||
/** CSI r */ setScrollRegion(params?: number[]): void;
|
/** CSI r */ setScrollRegion(params?: number[], collect?: string): void;
|
||||||
/** CSI s */ saveCursor(params?: number[]): void;
|
/** CSI s */ saveCursor(params?: number[]): void;
|
||||||
/** CSI u */ restoreCursor(params?: number[]): void;
|
/** CSI u */ restoreCursor(params?: number[]): void;
|
||||||
|
/** OSC 0
|
||||||
|
OSC 2 */ setTitle(data: string): void;
|
||||||
|
/** ESC E */ nextLine(): void;
|
||||||
|
/** ESC = */ keypadApplicationMode(): void;
|
||||||
|
/** ESC > */ keypadNumericMode(): void;
|
||||||
|
/** ESC % G
|
||||||
|
ESC % @ */ selectDefaultCharset(): void;
|
||||||
|
/** ESC ( C
|
||||||
|
ESC ) C
|
||||||
|
ESC * C
|
||||||
|
ESC + C
|
||||||
|
ESC - C
|
||||||
|
ESC . C
|
||||||
|
ESC / C */ selectCharset(collectAndFlag: string): void;
|
||||||
|
/** ESC D */ index(): void;
|
||||||
|
/** ESC H */ tabSet(): void;
|
||||||
|
/** ESC M */ reverseIndex(): void;
|
||||||
|
/** ESC c */ reset(): void;
|
||||||
|
/** ESC n
|
||||||
|
ESC o
|
||||||
|
ESC |
|
||||||
|
ESC }
|
||||||
|
ESC ~ */ setgLevel(level: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILinkMatcher {
|
export interface ILinkMatcher {
|
||||||
@@ -226,7 +249,7 @@ export interface ILinkifierAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IMouseHelper {
|
export interface IMouseHelper {
|
||||||
getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number];
|
getCoords(event: { pageX: number, pageY: number }, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number];
|
||||||
getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number };
|
getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,3 +378,134 @@ export interface IBrowser {
|
|||||||
export interface ISoundManager {
|
export interface ISoundManager {
|
||||||
playBellSound(): void;
|
playBellSound(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal states of EscapeSequenceParser.
|
||||||
|
*/
|
||||||
|
export const enum ParserState {
|
||||||
|
GROUND = 0,
|
||||||
|
ESCAPE = 1,
|
||||||
|
ESCAPE_INTERMEDIATE = 2,
|
||||||
|
CSI_ENTRY = 3,
|
||||||
|
CSI_PARAM = 4,
|
||||||
|
CSI_INTERMEDIATE = 5,
|
||||||
|
CSI_IGNORE = 6,
|
||||||
|
SOS_PM_APC_STRING = 7,
|
||||||
|
OSC_STRING = 8,
|
||||||
|
DCS_ENTRY = 9,
|
||||||
|
DCS_PARAM = 10,
|
||||||
|
DCS_IGNORE = 11,
|
||||||
|
DCS_INTERMEDIATE = 12,
|
||||||
|
DCS_PASSTHROUGH = 13
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal actions of EscapeSequenceParser.
|
||||||
|
*/
|
||||||
|
export const enum ParserAction {
|
||||||
|
IGNORE = 0,
|
||||||
|
ERROR = 1,
|
||||||
|
PRINT = 2,
|
||||||
|
EXECUTE = 3,
|
||||||
|
OSC_START = 4,
|
||||||
|
OSC_PUT = 5,
|
||||||
|
OSC_END = 6,
|
||||||
|
CSI_DISPATCH = 7,
|
||||||
|
PARAM = 8,
|
||||||
|
COLLECT = 9,
|
||||||
|
ESC_DISPATCH = 10,
|
||||||
|
CLEAR = 11,
|
||||||
|
DCS_HOOK = 12,
|
||||||
|
DCS_PUT = 13,
|
||||||
|
DCS_UNHOOK = 14
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal state of EscapeSequenceParser.
|
||||||
|
* Used as argument of the error handler to allow
|
||||||
|
* introspection at runtime on parse errors.
|
||||||
|
* Return it with altered values to recover from
|
||||||
|
* faulty states (not yet supported).
|
||||||
|
* Set `abort` to `true` to abort the current parsing.
|
||||||
|
*/
|
||||||
|
export interface IParsingState {
|
||||||
|
// position in parse string
|
||||||
|
position: number;
|
||||||
|
// actual character code
|
||||||
|
code: number;
|
||||||
|
// current parser state
|
||||||
|
currentState: ParserState;
|
||||||
|
// print buffer start index (-1 for not set)
|
||||||
|
print: number;
|
||||||
|
// dcs buffer start index (-1 for not set)
|
||||||
|
dcs: number;
|
||||||
|
// osc string buffer
|
||||||
|
osc: string;
|
||||||
|
// collect buffer with intermediate characters
|
||||||
|
collect: string;
|
||||||
|
// params buffer
|
||||||
|
params: number[];
|
||||||
|
// should abort (default: false)
|
||||||
|
abort: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS handler signature for EscapeSequenceParser.
|
||||||
|
* EscapeSequenceParser handles DCS commands via separate
|
||||||
|
* subparsers that get hook/unhooked and can handle
|
||||||
|
* arbitrary amount of print 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.
|
||||||
|
* EscapeSequenceParser will call `put` several times if the
|
||||||
|
* parsed string got splitted, therefore you might have to collect
|
||||||
|
* `data` until `unhook` is called. `unhook` marks the end
|
||||||
|
* of the current DCS sequence.
|
||||||
|
*/
|
||||||
|
export interface IDcsHandler {
|
||||||
|
hook(collect: string, params: number[], flag: number): void;
|
||||||
|
put(data: string, start: number, end: number): void;
|
||||||
|
unhook(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EscapeSequenceParser interface.
|
||||||
|
*/
|
||||||
|
export interface IEscapeSequenceParser {
|
||||||
|
/**
|
||||||
|
* Reset the parser to its initial state (handlers are kept).
|
||||||
|
*/
|
||||||
|
reset(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse string `data`.
|
||||||
|
* @param data The data to parse.
|
||||||
|
*/
|
||||||
|
parse(data: string): void;
|
||||||
|
|
||||||
|
setPrintHandler(callback: (data: string, start: number, end: number) => void): void;
|
||||||
|
clearPrintHandler(): void;
|
||||||
|
|
||||||
|
setExecuteHandler(flag: string, callback: () => void): void;
|
||||||
|
clearExecuteHandler(flag: string): void;
|
||||||
|
setExecuteHandlerFallback(callback: (code: number) => void): void;
|
||||||
|
|
||||||
|
setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void;
|
||||||
|
clearCsiHandler(flag: string): void;
|
||||||
|
setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void;
|
||||||
|
|
||||||
|
setEscHandler(collectAndFlag: string, callback: () => void): void;
|
||||||
|
clearEscHandler(collectAndFlag: string): void;
|
||||||
|
setEscHandlerFallback(callback: (collect: string, flag: number) => void): void;
|
||||||
|
|
||||||
|
setOscHandler(ident: number, callback: (data: string) => void): void;
|
||||||
|
clearOscHandler(ident: number): void;
|
||||||
|
setOscHandlerFallback(callback: (identifier: number, data: string) => void): void;
|
||||||
|
|
||||||
|
setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void;
|
||||||
|
clearDcsHandler(collectAndFlag: string): void;
|
||||||
|
setDcsHandlerFallback(handler: IDcsHandler): void;
|
||||||
|
|
||||||
|
setErrorHandler(callback: (state: IParsingState) => IParsingState): void;
|
||||||
|
clearErrorHandler(): void;
|
||||||
|
}
|
||||||
|
|||||||
@@ -184,7 +184,6 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
|||||||
bracketedPasteMode: boolean;
|
bracketedPasteMode: boolean;
|
||||||
defAttr: number;
|
defAttr: number;
|
||||||
curAttr: number;
|
curAttr: number;
|
||||||
prefix: string;
|
|
||||||
savedCols: number;
|
savedCols: number;
|
||||||
x10Mouse: boolean;
|
x10Mouse: boolean;
|
||||||
vt200Mouse: boolean;
|
vt200Mouse: boolean;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"parameter"
|
"parameter"
|
||||||
],
|
],
|
||||||
"eofline": true,
|
"eofline": true,
|
||||||
|
"new-parens": true,
|
||||||
"no-duplicate-imports": true,
|
"no-duplicate-imports": true,
|
||||||
"no-eval": true,
|
"no-eval": true,
|
||||||
"no-internal-module": true,
|
"no-internal-module": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user