+/**
+ * @license MIT
+ */
+
import { C0 } from './EscapeSequences';
import { IInputHandler } from './Interfaces';
-import { CHARSETS } from './Charsets';
+import { CHARSETS, DEFAULT_CHARSET } from './Charsets';
const normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {};
normalStateHandler[C0.BEL] = (parser, handler) => handler.bell();
escapedStateHandler['_'] = (parser, terminal) => {
// ESC _ Application Program Command ( APC is 0x9f).
parser.setState(ParserState.IGNORE);
-}
+};
escapedStateHandler['^'] = (parser, terminal) => {
// ESC ^ Privacy Message ( PM is 0x9e).
parser.setState(ParserState.IGNORE);
-}
+};
escapedStateHandler['c'] = (parser, terminal) => {
// ESC c Full Reset (RIS).
terminal.reset();
-}
+};
escapedStateHandler['E'] = (parser, terminal) => {
// ESC E Next Line ( NEL is 0x85).
terminal.x = 0;
terminal.index();
parser.setState(ParserState.NORMAL);
-}
+};
escapedStateHandler['D'] = (parser, terminal) => {
// ESC D Index ( IND is 0x84).
terminal.index();
// ESC % Select default/utf-8 character set.
// @ = default, G = utf-8
terminal.setgLevel(0);
- terminal.setgCharset(0, CHARSETS.US);
+ terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)
parser.setState(ParserState.NORMAL);
parser.skipNextChar();
};
+escapedStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);
const csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {};
csiParamStateHandler['?'] = (parser) => parser.setPrefix('?');
csiParamStateHandler[' '] = (parser) => parser.setPostfix(' ');
csiParamStateHandler['\''] = (parser) => parser.setPostfix('\'');
csiParamStateHandler[';'] = (parser) => parser.finalizeParam();
+csiParamStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL);
-const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string) => void} = {};
+const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string, postfix: string, parser: Parser) => void} = {};
csiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params);
csiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params);
csiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params);
case '!': handler.softReset(params); break;
}
};
+csiStateHandler['q'] = (handler, params, prefix, postfix) => {
+ if (postfix === ' ') {
+ handler.setCursorStyle(params);
+ }
+};
csiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params);
csiStateHandler['s'] = (handler, params) => handler.saveCursor(params);
csiStateHandler['u'] = (handler, params) => handler.restoreCursor(params);
+csiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL);
enum ParserState {
NORMAL = 0,
IGNORE = 7
}
+/**
+ * The terminal's parser, all input into the terminal goes through the parser
+ * which parses and defers the actual input handling the the IInputHandler
+ * specified in the constructor.
+ */
export class Parser {
private _state: ParserState;
private _position: number;
this._state = ParserState.NORMAL;
}
- public parse(data: string) {
+ /**
+ * Parse and handle data.
+ *
+ * @param data The data to parse.
+ */
+ public parse(data: string): ParserState {
let l = data.length, j, cs, ch, code, low;
this._position = 0;
break;
case ParserState.CHARSET:
- switch (ch) {
- case '0': // DEC Special Character and Line Drawing Set.
- cs = CHARSETS.SCLD;
- break;
- case 'A': // UK
- cs = CHARSETS.UK;
- break;
- case 'B': // United States (USASCII).
- cs = CHARSETS.US;
- break;
- case '4': // Dutch
- cs = CHARSETS.Dutch;
- break;
- case 'C': // Finnish
- case '5':
- cs = CHARSETS.Finnish;
- break;
- case 'R': // French
- cs = CHARSETS.French;
- break;
- case 'Q': // FrenchCanadian
- cs = CHARSETS.FrenchCanadian;
- break;
- case 'K': // German
- cs = CHARSETS.German;
- break;
- case 'Y': // Italian
- cs = CHARSETS.Italian;
- break;
- case 'E': // NorwegianDanish
- case '6':
- cs = CHARSETS.NorwegianDanish;
- break;
- case 'Z': // Spanish
- cs = CHARSETS.Spanish;
- break;
- case 'H': // Swedish
- case '7':
- cs = CHARSETS.Swedish;
- break;
- case '=': // Swiss
- cs = CHARSETS.Swiss;
- break;
- case '/': // ISOLatin (actually /A)
- cs = CHARSETS.ISOLatin;
- this._position++;
- break;
- default: // Default
- cs = CHARSETS.US;
- break;
+ if (ch in CHARSETS) {
+ cs = CHARSETS[ch];
+ if (ch === '/') { // ISOLatin is actually /A
+ this.skipNextChar();
+ }
+ } else {
+ cs = DEFAULT_CHARSET;
}
this._terminal.setgCharset(this._terminal.gcharset, cs);
this._terminal.gcharset = null;
case ParserState.CSI:
if (ch in csiStateHandler) {
- csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix);
+ csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this);
} else {
this._terminal.error('Unknown CSI code: %s.', ch);
}
case ParserState.DCS:
if (ch === C0.ESC || ch === C0.BEL) {
if (ch === C0.ESC) this._position++;
+ let pt;
+ let valid: boolean;
switch (this._terminal.prefix) {
// User-Defined Keys (DECUDK).
// Request Status String (DECRQSS).
// test: echo -e '\eP$q"p\e\\'
case '$q':
- let pt = this._terminal.currentParam
- , valid = false;
+ pt = this._terminal.currentParam;
+ valid = false;
switch (pt) {
// DECSCA
// This can cause a small glitch in vim.
// test: echo -ne '\eP+q6b64\e\\'
case '+q':
- // TODO: Don't declare pt twice
- /*let*/ pt = this._terminal.currentParam
- , valid = false;
+ pt = this._terminal.currentParam;
+ valid = false;
this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\');
break;
break;
}
}
+ return this._state;
}
+ /**
+ * Set the parser's current parsing state.
+ *
+ * @param state The new state.
+ */
public setState(state: ParserState): void {
this._state = state;
}
+ /**
+ * Sets the parsier's current prefix. CSI codes can have prefixes of '?', '>'
+ * or '!'.
+ *
+ * @param prefix The prefix.
+ */
public setPrefix(prefix: string): void {
this._terminal.prefix = prefix;
}
+ /**
+ * Sets the parsier's current prefix. CSI codes can have postfixes of '$',
+ * '"', ' ', '\''.
+ *
+ * @param postfix The postfix.
+ */
+ public setPostfix(postfix: string): void {
+ this._terminal.postfix = postfix;
+ }
+
+ /**
+ * Sets the parser's current parameter.
+ *
+ * @param param the parameter.
+ */
public setParam(param: number) {
this._terminal.currentParam = param;
}
+ /**
+ * Gets the parser's current parameter.
+ */
public getParam(): number {
return this._terminal.currentParam;
}
+ /**
+ * Finalizes the parser's current parameter, adding it to the list of
+ * parameters and setting the new current parameter to 0.
+ */
public finalizeParam(): void {
this._terminal.params.push(this._terminal.currentParam);
this._terminal.currentParam = 0;
}
- public setPostfix(postfix: string): void {
- this._terminal.postfix = postfix;
- }
-
+ /**
+ * Tell the parser to skip the next character.
+ */
public skipNextChar(): void {
this._position++;
}
+ /**
+ * Tell the parser to repeat parsing the current character (for example if it
+ * needs parsing using a different state.
+ */
// public repeatChar(): void {
// this._position--;
// }