diff --git a/src/InputHandler.ts b/src/InputHandler.ts index e8f476703b..509e3697e2 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -4,17 +4,18 @@ * @license MIT */ -import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IInputHandlingTerminal } from './Types'; +import { IInputHandler, IInputHandlingTerminal } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'core/data/Charsets'; import { wcwidth } from './CharWidth'; -import { EscapeSequenceParser } from './EscapeSequenceParser'; +import { EscapeSequenceParser } from 'core/parser/EscapeSequenceParser'; import { IDisposable } from 'xterm'; import { Disposable } from 'common/Lifecycle'; import { concat } from 'common/TypedArrayUtils'; import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'core/input/TextDecoder'; import { CellData, Attributes, FgFlags, BgFlags, AttributeData, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from 'core/buffer/BufferLine'; import { EventEmitter2, IEvent } from 'common/EventEmitter2'; +import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'core/parser/Types'; /** * Map collect to glevel. Used in `selectCharset`. @@ -277,7 +278,7 @@ export class InputHandler extends Disposable implements IInputHandler { /** * error handler */ - this._parser.setErrorHandler((state) => { + this._parser.setErrorHandler((state: IParsingState) => { this._terminal.error('Parsing error: ', state); return state; }); diff --git a/src/Types.ts b/src/Types.ts index 7e01b99f52..0ef623d33d 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -434,147 +434,6 @@ export interface ISoundManager { 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 data. -* -* On entering a DSC sequence `hook` is called by -* `EscapeSequenceParser`. Use it to initialize or reset -* states needed to handle the current DCS sequence. -* Note: A DCS parser is only instantiated once, therefore -* you cannot rely on the ctor to reinitialize state. -* -* EscapeSequenceParser will call `put` several times if the -* parsed data got split, therefore you might have to collect -* `data` until `unhook` is called. -* Note: `data` is borrowed, if you cannot process the data -* in chunks you have to copy it, doing otherwise will lead to -* data losses or corruption. -* -* `unhook` marks the end of the current DCS sequence. -*/ -export interface IDcsHandler { - hook(collect: string, params: number[], flag: number): void; - put(data: Uint32Array, start: number, end: number): void; - unhook(): void; -} - -/** -* EscapeSequenceParser interface. -*/ -export interface IEscapeSequenceParser extends IDisposable { - /** - * Reset the parser to its initial state (handlers are kept). - */ - reset(): void; - - /** - * Parse string `data`. - * @param data The data to parse. - */ - parse(data: Uint32Array, length: number): void; - - setPrintHandler(callback: (data: Uint32Array, 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; - addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; - addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; - - setEscHandler(collectAndFlag: string, callback: () => void): void; - clearEscHandler(collectAndFlag: string): void; - 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; -} - export interface IMouseZoneManager extends IDisposable { add(zone: IMouseZone): void; clearAll(start?: number, end?: number): void; diff --git a/src/EscapeSequenceParser.test.ts b/src/core/parser/EscapeSequenceParser.test.ts similarity index 99% rename from src/EscapeSequenceParser.test.ts rename to src/core/parser/EscapeSequenceParser.test.ts index fec56b6253..553050c854 100644 --- a/src/EscapeSequenceParser.test.ts +++ b/src/core/parser/EscapeSequenceParser.test.ts @@ -3,8 +3,8 @@ * @license MIT */ -import { ParserState, IDcsHandler, IParsingState } from './Types'; -import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from './EscapeSequenceParser'; +import { ParserState, IDcsHandler, IParsingState } from 'core/parser/Types'; +import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from 'core/parser/EscapeSequenceParser'; import * as chai from 'chai'; import { StringToUtf32, stringFromCodePoint } from 'core/input/TextDecoder'; @@ -172,7 +172,7 @@ function parse(parser: TestEscapeSequenceParser, data: string): void { } describe('EscapeSequenceParser', function (): void { - let parser: TestEscapeSequenceParser | null = null; + let parser: TestEscapeSequenceParser; const runs: IRun[] = [ { tableType: 'Uint8Array', parser: parserUint }, { tableType: 'Array', parser: parserArray } @@ -995,7 +995,7 @@ describe('EscapeSequenceParser', function (): void { }); runs.forEach(function (run: IRun): void { - let test: Function | null = null; + let test: Function; describe('escape sequence examples / ' + run.tableType, function (): void { before(function(): void { parser = run.parser; @@ -1122,7 +1122,7 @@ describe('EscapeSequenceParser', function (): void { describe('set/clear handler', function (): void { const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1b]1;foo=bar\x1b\\'; - let parser2: TestEscapeSequenceParser = null; + let parser2: TestEscapeSequenceParser; let print = ''; const esc: string[] = []; const csi: [string, number[], string][] = []; @@ -1378,7 +1378,7 @@ describe('EscapeSequenceParser', function (): void { chai.expect(dcs).eql([]); }); it('ERROR handler', function (): void { - let errorState: IParsingState = null; + let errorState: IParsingState | null = null; parser2.setErrorHandler(function (state: IParsingState): IParsingState { errorState = state; return state; diff --git a/src/EscapeSequenceParser.ts b/src/core/parser/EscapeSequenceParser.ts similarity index 97% rename from src/EscapeSequenceParser.ts rename to src/core/parser/EscapeSequenceParser.ts index 09ffc8d60e..7929b2f06c 100644 --- a/src/EscapeSequenceParser.ts +++ b/src/core/parser/EscapeSequenceParser.ts @@ -3,10 +3,10 @@ * @license MIT */ -import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types'; -import { IDisposable } from 'xterm'; +import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from 'core/parser/Types'; import { Disposable } from 'common/Lifecycle'; import { utf32ToString } from 'core/input/TextDecoder'; +import { IDisposable } from 'common/Types'; interface IHandlerCollection { [key: string]: T[]; @@ -50,8 +50,8 @@ export class TransitionTable { * @param action parser action to be done * @param next next parser state */ - add(code: number, state: number, action: number | null, next: number | null): void { - this.table[state << 8 | code] = ((action | 0) << 4) | ((next === undefined) ? state : next); + add(code: number, state: ParserState, action: ParserAction, next: ParserState): void { + this.table[state << 8 | code] = (action << 4) | next; } /** @@ -61,7 +61,7 @@ export class TransitionTable { * @param action parser action to be done * @param next next parser state */ - addMany(codes: number[], state: number, action: number | null, next: number | null): void { + addMany(codes: number[], state: ParserState, action: ParserAction, next: ParserState): void { for (let i = 0; i < codes.length; i++) { this.add(codes[i], state, action, next); } @@ -279,21 +279,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } public dispose(): void { - this._printHandlerFb = null; - this._executeHandlerFb = null; - this._csiHandlerFb = null; - this._escHandlerFb = null; - this._oscHandlerFb = null; - this._dcsHandlerFb = null; - this._errorHandlerFb = null; - this._printHandler = null; this._executeHandlers = null; this._escHandlers = null; - this._csiHandlers = null; - this._oscHandlers = null; this._dcsHandlers = null; this._activeDcsHandler = null; - this._errorHandler = null; } setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void { @@ -453,7 +442,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP this._printHandler(data, print, i); print = -1; } else if (~dcs) { - dcsHandler.put(data, dcs, i); + if (dcsHandler) { + dcsHandler.put(data, dcs, i); + } dcs = -1; } break; diff --git a/src/core/parser/Types.ts b/src/core/parser/Types.ts new file mode 100644 index 0000000000..c208496aa1 --- /dev/null +++ b/src/core/parser/Types.ts @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IDisposable } from 'common/Types'; + +/** + * 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 data. +* +* On entering a DSC sequence `hook` is called by +* `EscapeSequenceParser`. Use it to initialize or reset +* states needed to handle the current DCS sequence. +* Note: A DCS parser is only instantiated once, therefore +* you cannot rely on the ctor to reinitialize state. +* +* EscapeSequenceParser will call `put` several times if the +* parsed data got split, therefore you might have to collect +* `data` until `unhook` is called. +* Note: `data` is borrowed, if you cannot process the data +* in chunks you have to copy it, doing otherwise will lead to +* data losses or corruption. +* +* `unhook` marks the end of the current DCS sequence. +*/ +export interface IDcsHandler { + hook(collect: string, params: number[], flag: number): void; + put(data: Uint32Array, start: number, end: number): void; + unhook(): void; +} + +/** +* EscapeSequenceParser interface. +*/ +export interface IEscapeSequenceParser extends IDisposable { + /** + * Reset the parser to its initial state (handlers are kept). + */ + reset(): void; + + /** + * Parse string `data`. + * @param data The data to parse. + */ + parse(data: Uint32Array, length: number): void; + + setPrintHandler(callback: (data: Uint32Array, 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; + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; + + setEscHandler(collectAndFlag: string, callback: () => void): void; + clearEscHandler(collectAndFlag: string): void; + 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; +}