Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move EscapeSequenceParser to core #2168

Merged
merged 3 commits into from
Jun 1, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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;
});
Expand Down
141 changes: 0 additions & 141 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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][] = [];
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
[key: string]: T[];
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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);
}
Expand Down Expand Up @@ -279,21 +279,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
}

public dispose(): void {
this._printHandlerFb = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we cannot (yet) remove these as they might hold a reference from the input handler with a terminal object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in that case it will still get disposed correctly and release the reference to input handler and terminal when the consumers reference to terminal is removed

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 {
Expand Down Expand Up @@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there an error regarding this? In theory dcsHandler should never be null here (if so the parser has a serious logic flaw). I am abit unhappy about dcsHandler being null in between, guess this needs some refactoring (to reduce branching).
Well its good for now with this addtional check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dcsHandler could be null according to the type system, if we're certain and want to avoid the if we could compel the type system with !, that's unsafe though.

dcsHandler.put(data, dcs, i);
}
dcs = -1;
}
break;
Expand Down