]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | * @license MIT | |
3 | */ | |
4 | ||
5 | import { C0 } from './EscapeSequences'; | |
6 | import { IInputHandler } from './Interfaces'; | |
7 | import { CHARSETS, DEFAULT_CHARSET } from './Charsets'; | |
8 | ||
9 | const normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {}; | |
10 | normalStateHandler[C0.BEL] = (parser, handler) => handler.bell(); | |
11 | normalStateHandler[C0.LF] = (parser, handler) => handler.lineFeed(); | |
12 | normalStateHandler[C0.VT] = normalStateHandler[C0.LF]; | |
13 | normalStateHandler[C0.FF] = normalStateHandler[C0.LF]; | |
14 | normalStateHandler[C0.CR] = (parser, handler) => handler.carriageReturn(); | |
15 | normalStateHandler[C0.BS] = (parser, handler) => handler.backspace(); | |
16 | normalStateHandler[C0.HT] = (parser, handler) => handler.tab(); | |
17 | normalStateHandler[C0.SO] = (parser, handler) => handler.shiftOut(); | |
18 | normalStateHandler[C0.SI] = (parser, handler) => handler.shiftIn(); | |
19 | normalStateHandler[C0.ESC] = (parser, handler) => parser.setState(ParserState.ESCAPED); | |
20 | ||
21 | // TODO: Remove terminal when parser owns params and currentParam | |
22 | const escapedStateHandler: {[key: string]: (parser: Parser, terminal: any) => void} = {}; | |
23 | escapedStateHandler['['] = (parser, terminal) => { | |
24 | // ESC [ Control Sequence Introducer (CSI is 0x9b) | |
25 | terminal.params = []; | |
26 | terminal.currentParam = 0; | |
27 | parser.setState(ParserState.CSI_PARAM); | |
28 | }; | |
29 | escapedStateHandler[']'] = (parser, terminal) => { | |
30 | // ESC ] Operating System Command (OSC is 0x9d) | |
31 | terminal.params = []; | |
32 | terminal.currentParam = 0; | |
33 | parser.setState(ParserState.OSC); | |
34 | }; | |
35 | escapedStateHandler['P'] = (parser, terminal) => { | |
36 | // ESC P Device Control String (DCS is 0x90) | |
37 | terminal.params = []; | |
38 | terminal.currentParam = 0; | |
39 | parser.setState(ParserState.DCS); | |
40 | }; | |
41 | escapedStateHandler['_'] = (parser, terminal) => { | |
42 | // ESC _ Application Program Command ( APC is 0x9f). | |
43 | parser.setState(ParserState.IGNORE); | |
44 | }; | |
45 | escapedStateHandler['^'] = (parser, terminal) => { | |
46 | // ESC ^ Privacy Message ( PM is 0x9e). | |
47 | parser.setState(ParserState.IGNORE); | |
48 | }; | |
49 | escapedStateHandler['c'] = (parser, terminal) => { | |
50 | // ESC c Full Reset (RIS). | |
51 | terminal.reset(); | |
52 | }; | |
53 | escapedStateHandler['E'] = (parser, terminal) => { | |
54 | // ESC E Next Line ( NEL is 0x85). | |
55 | terminal.x = 0; | |
56 | terminal.index(); | |
57 | parser.setState(ParserState.NORMAL); | |
58 | }; | |
59 | escapedStateHandler['D'] = (parser, terminal) => { | |
60 | // ESC D Index ( IND is 0x84). | |
61 | terminal.index(); | |
62 | parser.setState(ParserState.NORMAL); | |
63 | }; | |
64 | escapedStateHandler['M'] = (parser, terminal) => { | |
65 | // ESC M Reverse Index ( RI is 0x8d). | |
66 | terminal.reverseIndex(); | |
67 | parser.setState(ParserState.NORMAL); | |
68 | }; | |
69 | escapedStateHandler['%'] = (parser, terminal) => { | |
70 | // ESC % Select default/utf-8 character set. | |
71 | // @ = default, G = utf-8 | |
72 | terminal.setgLevel(0); | |
73 | terminal.setgCharset(0, DEFAULT_CHARSET); // US (default) | |
74 | parser.setState(ParserState.NORMAL); | |
75 | parser.skipNextChar(); | |
76 | }; | |
77 | escapedStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL); | |
78 | ||
79 | const csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {}; | |
80 | csiParamStateHandler['?'] = (parser) => parser.setPrefix('?'); | |
81 | csiParamStateHandler['>'] = (parser) => parser.setPrefix('>'); | |
82 | csiParamStateHandler['!'] = (parser) => parser.setPrefix('!'); | |
83 | csiParamStateHandler['0'] = (parser) => parser.setParam(parser.getParam() * 10); | |
84 | csiParamStateHandler['1'] = (parser) => parser.setParam(parser.getParam() * 10 + 1); | |
85 | csiParamStateHandler['2'] = (parser) => parser.setParam(parser.getParam() * 10 + 2); | |
86 | csiParamStateHandler['3'] = (parser) => parser.setParam(parser.getParam() * 10 + 3); | |
87 | csiParamStateHandler['4'] = (parser) => parser.setParam(parser.getParam() * 10 + 4); | |
88 | csiParamStateHandler['5'] = (parser) => parser.setParam(parser.getParam() * 10 + 5); | |
89 | csiParamStateHandler['6'] = (parser) => parser.setParam(parser.getParam() * 10 + 6); | |
90 | csiParamStateHandler['7'] = (parser) => parser.setParam(parser.getParam() * 10 + 7); | |
91 | csiParamStateHandler['8'] = (parser) => parser.setParam(parser.getParam() * 10 + 8); | |
92 | csiParamStateHandler['9'] = (parser) => parser.setParam(parser.getParam() * 10 + 9); | |
93 | csiParamStateHandler['$'] = (parser) => parser.setPostfix('$'); | |
94 | csiParamStateHandler['"'] = (parser) => parser.setPostfix('"'); | |
95 | csiParamStateHandler[' '] = (parser) => parser.setPostfix(' '); | |
96 | csiParamStateHandler['\''] = (parser) => parser.setPostfix('\''); | |
97 | csiParamStateHandler[';'] = (parser) => parser.finalizeParam(); | |
98 | csiParamStateHandler[C0.CAN] = (parser) => parser.setState(ParserState.NORMAL); | |
99 | ||
100 | const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string, postfix: string, parser: Parser) => void} = {}; | |
101 | csiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params); | |
102 | csiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params); | |
103 | csiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params); | |
104 | csiStateHandler['C'] = (handler, params, prefix) => handler.cursorForward(params); | |
105 | csiStateHandler['D'] = (handler, params, prefix) => handler.cursorBackward(params); | |
106 | csiStateHandler['E'] = (handler, params, prefix) => handler.cursorNextLine(params); | |
107 | csiStateHandler['F'] = (handler, params, prefix) => handler.cursorPrecedingLine(params); | |
108 | csiStateHandler['G'] = (handler, params, prefix) => handler.cursorCharAbsolute(params); | |
109 | csiStateHandler['H'] = (handler, params, prefix) => handler.cursorPosition(params); | |
110 | csiStateHandler['I'] = (handler, params, prefix) => handler.cursorForwardTab(params); | |
111 | csiStateHandler['J'] = (handler, params, prefix) => handler.eraseInDisplay(params); | |
112 | csiStateHandler['K'] = (handler, params, prefix) => handler.eraseInLine(params); | |
113 | csiStateHandler['L'] = (handler, params, prefix) => handler.insertLines(params); | |
114 | csiStateHandler['M'] = (handler, params, prefix) => handler.deleteLines(params); | |
115 | csiStateHandler['P'] = (handler, params, prefix) => handler.deleteChars(params); | |
116 | csiStateHandler['S'] = (handler, params, prefix) => handler.scrollUp(params); | |
117 | csiStateHandler['T'] = (handler, params, prefix) => { | |
118 | if (params.length < 2 && !prefix) { | |
119 | handler.scrollDown(params); | |
120 | } | |
121 | }; | |
122 | csiStateHandler['X'] = (handler, params, prefix) => handler.eraseChars(params); | |
123 | csiStateHandler['Z'] = (handler, params, prefix) => handler.cursorBackwardTab(params); | |
124 | csiStateHandler['`'] = (handler, params, prefix) => handler.charPosAbsolute(params); | |
125 | csiStateHandler['a'] = (handler, params, prefix) => handler.HPositionRelative(params); | |
126 | csiStateHandler['b'] = (handler, params, prefix) => handler.repeatPrecedingCharacter(params); | |
127 | csiStateHandler['c'] = (handler, params, prefix) => handler.sendDeviceAttributes(params); | |
128 | csiStateHandler['d'] = (handler, params, prefix) => handler.linePosAbsolute(params); | |
129 | csiStateHandler['e'] = (handler, params, prefix) => handler.VPositionRelative(params); | |
130 | csiStateHandler['f'] = (handler, params, prefix) => handler.HVPosition(params); | |
131 | csiStateHandler['g'] = (handler, params, prefix) => handler.tabClear(params); | |
132 | csiStateHandler['h'] = (handler, params, prefix) => handler.setMode(params); | |
133 | csiStateHandler['l'] = (handler, params, prefix) => handler.resetMode(params); | |
134 | csiStateHandler['m'] = (handler, params, prefix) => handler.charAttributes(params); | |
135 | csiStateHandler['n'] = (handler, params, prefix) => handler.deviceStatus(params); | |
136 | csiStateHandler['p'] = (handler, params, prefix) => { | |
137 | switch (prefix) { | |
138 | case '!': handler.softReset(params); break; | |
139 | } | |
140 | }; | |
141 | csiStateHandler['q'] = (handler, params, prefix, postfix) => { | |
142 | if (postfix === ' ') { | |
143 | handler.setCursorStyle(params); | |
144 | } | |
145 | }; | |
146 | csiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params); | |
147 | csiStateHandler['s'] = (handler, params) => handler.saveCursor(params); | |
148 | csiStateHandler['u'] = (handler, params) => handler.restoreCursor(params); | |
149 | csiStateHandler[C0.CAN] = (handler, params, prefix, postfix, parser) => parser.setState(ParserState.NORMAL); | |
150 | ||
151 | enum ParserState { | |
152 | NORMAL = 0, | |
153 | ESCAPED = 1, | |
154 | CSI_PARAM = 2, | |
155 | CSI = 3, | |
156 | OSC = 4, | |
157 | CHARSET = 5, | |
158 | DCS = 6, | |
159 | IGNORE = 7 | |
160 | } | |
161 | ||
162 | /** | |
163 | * The terminal's parser, all input into the terminal goes through the parser | |
164 | * which parses and defers the actual input handling the the IInputHandler | |
165 | * specified in the constructor. | |
166 | */ | |
167 | export class Parser { | |
168 | private _state: ParserState; | |
169 | private _position: number; | |
170 | ||
171 | // TODO: Remove terminal when handler can do everything | |
172 | constructor( | |
173 | private _inputHandler: IInputHandler, | |
174 | private _terminal: any | |
175 | ) { | |
176 | this._state = ParserState.NORMAL; | |
177 | } | |
178 | ||
179 | /** | |
180 | * Parse and handle data. | |
181 | * | |
182 | * @param data The data to parse. | |
183 | */ | |
184 | public parse(data: string): ParserState { | |
185 | let l = data.length, j, cs, ch, code, low; | |
186 | ||
187 | this._position = 0; | |
188 | // apply leftover surrogate high from last write | |
189 | if (this._terminal.surrogate_high) { | |
190 | data = this._terminal.surrogate_high + data; | |
191 | this._terminal.surrogate_high = ''; | |
192 | } | |
193 | ||
194 | for (; this._position < l; this._position++) { | |
195 | ch = data[this._position]; | |
196 | ||
197 | // FIXME: higher chars than 0xa0 are not allowed in escape sequences | |
198 | // --> maybe move to default | |
199 | code = data.charCodeAt(this._position); | |
200 | if (0xD800 <= code && code <= 0xDBFF) { | |
201 | // we got a surrogate high | |
202 | // get surrogate low (next 2 bytes) | |
203 | low = data.charCodeAt(this._position + 1); | |
204 | if (isNaN(low)) { | |
205 | // end of data stream, save surrogate high | |
206 | this._terminal.surrogate_high = ch; | |
207 | continue; | |
208 | } | |
209 | code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; | |
210 | ch += data.charAt(this._position + 1); | |
211 | } | |
212 | // surrogate low - already handled above | |
213 | if (0xDC00 <= code && code <= 0xDFFF) | |
214 | continue; | |
215 | ||
216 | switch (this._state) { | |
217 | case ParserState.NORMAL: | |
218 | if (ch in normalStateHandler) { | |
219 | normalStateHandler[ch](this, this._inputHandler); | |
220 | } else { | |
221 | this._inputHandler.addChar(ch, code); | |
222 | } | |
223 | break; | |
224 | case ParserState.ESCAPED: | |
225 | if (ch in escapedStateHandler) { | |
226 | escapedStateHandler[ch](this, this._terminal); | |
227 | // Skip switch as it was just handled | |
228 | break; | |
229 | } | |
230 | switch (ch) { | |
231 | ||
232 | // ESC (,),*,+,-,. Designate G0-G2 Character Set. | |
233 | case '(': // <-- this seems to get all the attention | |
234 | case ')': | |
235 | case '*': | |
236 | case '+': | |
237 | case '-': | |
238 | case '.': | |
239 | switch (ch) { | |
240 | case '(': | |
241 | this._terminal.gcharset = 0; | |
242 | break; | |
243 | case ')': | |
244 | this._terminal.gcharset = 1; | |
245 | break; | |
246 | case '*': | |
247 | this._terminal.gcharset = 2; | |
248 | break; | |
249 | case '+': | |
250 | this._terminal.gcharset = 3; | |
251 | break; | |
252 | case '-': | |
253 | this._terminal.gcharset = 1; | |
254 | break; | |
255 | case '.': | |
256 | this._terminal.gcharset = 2; | |
257 | break; | |
258 | } | |
259 | this._state = ParserState.CHARSET; | |
260 | break; | |
261 | ||
262 | // Designate G3 Character Set (VT300). | |
263 | // A = ISO Latin-1 Supplemental. | |
264 | // Not implemented. | |
265 | case '/': | |
266 | this._terminal.gcharset = 3; | |
267 | this._state = ParserState.CHARSET; | |
268 | this._position--; | |
269 | break; | |
270 | ||
271 | // ESC N | |
272 | // Single Shift Select of G2 Character Set | |
273 | // ( SS2 is 0x8e). This affects next character only. | |
274 | case 'N': | |
275 | break; | |
276 | // ESC O | |
277 | // Single Shift Select of G3 Character Set | |
278 | // ( SS3 is 0x8f). This affects next character only. | |
279 | case 'O': | |
280 | break; | |
281 | // ESC n | |
282 | // Invoke the G2 Character Set as GL (LS2). | |
283 | case 'n': | |
284 | this._terminal.setgLevel(2); | |
285 | break; | |
286 | // ESC o | |
287 | // Invoke the G3 Character Set as GL (LS3). | |
288 | case 'o': | |
289 | this._terminal.setgLevel(3); | |
290 | break; | |
291 | // ESC | | |
292 | // Invoke the G3 Character Set as GR (LS3R). | |
293 | case '|': | |
294 | this._terminal.setgLevel(3); | |
295 | break; | |
296 | // ESC } | |
297 | // Invoke the G2 Character Set as GR (LS2R). | |
298 | case '}': | |
299 | this._terminal.setgLevel(2); | |
300 | break; | |
301 | // ESC ~ | |
302 | // Invoke the G1 Character Set as GR (LS1R). | |
303 | case '~': | |
304 | this._terminal.setgLevel(1); | |
305 | break; | |
306 | ||
307 | // ESC 7 Save Cursor (DECSC). | |
308 | case '7': | |
309 | this._inputHandler.saveCursor(); | |
310 | this._state = ParserState.NORMAL; | |
311 | break; | |
312 | ||
313 | // ESC 8 Restore Cursor (DECRC). | |
314 | case '8': | |
315 | this._inputHandler.restoreCursor(); | |
316 | this._state = ParserState.NORMAL; | |
317 | break; | |
318 | ||
319 | // ESC # 3 DEC line height/width | |
320 | case '#': | |
321 | this._state = ParserState.NORMAL; | |
322 | this._position++; | |
323 | break; | |
324 | ||
325 | // ESC H Tab Set (HTS is 0x88). | |
326 | case 'H': | |
327 | this._terminal.tabSet(); | |
328 | this._state = ParserState.NORMAL; | |
329 | break; | |
330 | ||
331 | // ESC = Application Keypad (DECKPAM). | |
332 | case '=': | |
333 | this._terminal.log('Serial port requested application keypad.'); | |
334 | this._terminal.applicationKeypad = true; | |
335 | this._terminal.viewport.syncScrollArea(); | |
336 | this._state = ParserState.NORMAL; | |
337 | break; | |
338 | ||
339 | // ESC > Normal Keypad (DECKPNM). | |
340 | case '>': | |
341 | this._terminal.log('Switching back to normal keypad.'); | |
342 | this._terminal.applicationKeypad = false; | |
343 | this._terminal.viewport.syncScrollArea(); | |
344 | this._state = ParserState.NORMAL; | |
345 | break; | |
346 | ||
347 | default: | |
348 | this._state = ParserState.NORMAL; | |
349 | this._terminal.error('Unknown ESC control: %s.', ch); | |
350 | break; | |
351 | } | |
352 | break; | |
353 | ||
354 | case ParserState.CHARSET: | |
355 | if (ch in CHARSETS) { | |
356 | cs = CHARSETS[ch]; | |
357 | if (ch === '/') { // ISOLatin is actually /A | |
358 | this.skipNextChar(); | |
359 | } | |
360 | } else { | |
361 | cs = DEFAULT_CHARSET; | |
362 | } | |
363 | this._terminal.setgCharset(this._terminal.gcharset, cs); | |
364 | this._terminal.gcharset = null; | |
365 | this._state = ParserState.NORMAL; | |
366 | break; | |
367 | ||
368 | case ParserState.OSC: | |
369 | // OSC Ps ; Pt ST | |
370 | // OSC Ps ; Pt BEL | |
371 | // Set Text Parameters. | |
372 | if (ch === C0.ESC || ch === C0.BEL) { | |
373 | if (ch === C0.ESC) this._position++; | |
374 | ||
375 | this._terminal.params.push(this._terminal.currentParam); | |
376 | ||
377 | switch (this._terminal.params[0]) { | |
378 | case 0: | |
379 | case 1: | |
380 | case 2: | |
381 | if (this._terminal.params[1]) { | |
382 | this._terminal.title = this._terminal.params[1]; | |
383 | this._terminal.handleTitle(this._terminal.title); | |
384 | } | |
385 | break; | |
386 | case 3: | |
387 | // set X property | |
388 | break; | |
389 | case 4: | |
390 | case 5: | |
391 | // change dynamic colors | |
392 | break; | |
393 | case 10: | |
394 | case 11: | |
395 | case 12: | |
396 | case 13: | |
397 | case 14: | |
398 | case 15: | |
399 | case 16: | |
400 | case 17: | |
401 | case 18: | |
402 | case 19: | |
403 | // change dynamic ui colors | |
404 | break; | |
405 | case 46: | |
406 | // change log file | |
407 | break; | |
408 | case 50: | |
409 | // dynamic font | |
410 | break; | |
411 | case 51: | |
412 | // emacs shell | |
413 | break; | |
414 | case 52: | |
415 | // manipulate selection data | |
416 | break; | |
417 | case 104: | |
418 | case 105: | |
419 | case 110: | |
420 | case 111: | |
421 | case 112: | |
422 | case 113: | |
423 | case 114: | |
424 | case 115: | |
425 | case 116: | |
426 | case 117: | |
427 | case 118: | |
428 | // reset colors | |
429 | break; | |
430 | } | |
431 | ||
432 | this._terminal.params = []; | |
433 | this._terminal.currentParam = 0; | |
434 | this._state = ParserState.NORMAL; | |
435 | } else { | |
436 | if (!this._terminal.params.length) { | |
437 | if (ch >= '0' && ch <= '9') { | |
438 | this._terminal.currentParam = | |
439 | this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48; | |
440 | } else if (ch === ';') { | |
441 | this._terminal.params.push(this._terminal.currentParam); | |
442 | this._terminal.currentParam = ''; | |
443 | } | |
444 | } else { | |
445 | this._terminal.currentParam += ch; | |
446 | } | |
447 | } | |
448 | break; | |
449 | ||
450 | case ParserState.CSI_PARAM: | |
451 | if (ch in csiParamStateHandler) { | |
452 | csiParamStateHandler[ch](this); | |
453 | break; | |
454 | } | |
455 | this.finalizeParam(); | |
456 | // Fall through the CSI as this character should be the CSI code. | |
457 | this._state = ParserState.CSI; | |
458 | ||
459 | case ParserState.CSI: | |
460 | if (ch in csiStateHandler) { | |
461 | csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix, this._terminal.postfix, this); | |
462 | } else { | |
463 | this._terminal.error('Unknown CSI code: %s.', ch); | |
464 | } | |
465 | ||
466 | this._state = ParserState.NORMAL; | |
467 | this._terminal.prefix = ''; | |
468 | this._terminal.postfix = ''; | |
469 | break; | |
470 | ||
471 | case ParserState.DCS: | |
472 | if (ch === C0.ESC || ch === C0.BEL) { | |
473 | if (ch === C0.ESC) this._position++; | |
474 | let pt; | |
475 | let valid: boolean; | |
476 | ||
477 | switch (this._terminal.prefix) { | |
478 | // User-Defined Keys (DECUDK). | |
479 | case '': | |
480 | break; | |
481 | ||
482 | // Request Status String (DECRQSS). | |
483 | // test: echo -e '\eP$q"p\e\\' | |
484 | case '$q': | |
485 | pt = this._terminal.currentParam; | |
486 | valid = false; | |
487 | ||
488 | switch (pt) { | |
489 | // DECSCA | |
490 | case '"q': | |
491 | pt = '0"q'; | |
492 | break; | |
493 | ||
494 | // DECSCL | |
495 | case '"p': | |
496 | pt = '61"p'; | |
497 | break; | |
498 | ||
499 | // DECSTBM | |
500 | case 'r': | |
501 | pt = '' | |
502 | + (this._terminal.scrollTop + 1) | |
503 | + ';' | |
504 | + (this._terminal.scrollBottom + 1) | |
505 | + 'r'; | |
506 | break; | |
507 | ||
508 | // SGR | |
509 | case 'm': | |
510 | pt = '0m'; | |
511 | break; | |
512 | ||
513 | default: | |
514 | this._terminal.error('Unknown DCS Pt: %s.', pt); | |
515 | pt = ''; | |
516 | break; | |
517 | } | |
518 | ||
519 | this._terminal.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\'); | |
520 | break; | |
521 | ||
522 | // Set Termcap/Terminfo Data (xterm, experimental). | |
523 | case '+p': | |
524 | break; | |
525 | ||
526 | // Request Termcap/Terminfo String (xterm, experimental) | |
527 | // Regular xterm does not even respond to this sequence. | |
528 | // This can cause a small glitch in vim. | |
529 | // test: echo -ne '\eP+q6b64\e\\' | |
530 | case '+q': | |
531 | pt = this._terminal.currentParam; | |
532 | valid = false; | |
533 | ||
534 | this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\'); | |
535 | break; | |
536 | ||
537 | default: | |
538 | this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix); | |
539 | break; | |
540 | } | |
541 | ||
542 | this._terminal.currentParam = 0; | |
543 | this._terminal.prefix = ''; | |
544 | this._state = ParserState.NORMAL; | |
545 | } else if (!this._terminal.currentParam) { | |
546 | if (!this._terminal.prefix && ch !== '$' && ch !== '+') { | |
547 | this._terminal.currentParam = ch; | |
548 | } else if (this._terminal.prefix.length === 2) { | |
549 | this._terminal.currentParam = ch; | |
550 | } else { | |
551 | this._terminal.prefix += ch; | |
552 | } | |
553 | } else { | |
554 | this._terminal.currentParam += ch; | |
555 | } | |
556 | break; | |
557 | ||
558 | case ParserState.IGNORE: | |
559 | // For PM and APC. | |
560 | if (ch === C0.ESC || ch === C0.BEL) { | |
561 | if (ch === C0.ESC) this._position++; | |
562 | this._state = ParserState.NORMAL; | |
563 | } | |
564 | break; | |
565 | } | |
566 | } | |
567 | return this._state; | |
568 | } | |
569 | ||
570 | /** | |
571 | * Set the parser's current parsing state. | |
572 | * | |
573 | * @param state The new state. | |
574 | */ | |
575 | public setState(state: ParserState): void { | |
576 | this._state = state; | |
577 | } | |
578 | ||
579 | /** | |
580 | * Sets the parsier's current prefix. CSI codes can have prefixes of '?', '>' | |
581 | * or '!'. | |
582 | * | |
583 | * @param prefix The prefix. | |
584 | */ | |
585 | public setPrefix(prefix: string): void { | |
586 | this._terminal.prefix = prefix; | |
587 | } | |
588 | ||
589 | /** | |
590 | * Sets the parsier's current prefix. CSI codes can have postfixes of '$', | |
591 | * '"', ' ', '\''. | |
592 | * | |
593 | * @param postfix The postfix. | |
594 | */ | |
595 | public setPostfix(postfix: string): void { | |
596 | this._terminal.postfix = postfix; | |
597 | } | |
598 | ||
599 | /** | |
600 | * Sets the parser's current parameter. | |
601 | * | |
602 | * @param param the parameter. | |
603 | */ | |
604 | public setParam(param: number) { | |
605 | this._terminal.currentParam = param; | |
606 | } | |
607 | ||
608 | /** | |
609 | * Gets the parser's current parameter. | |
610 | */ | |
611 | public getParam(): number { | |
612 | return this._terminal.currentParam; | |
613 | } | |
614 | ||
615 | /** | |
616 | * Finalizes the parser's current parameter, adding it to the list of | |
617 | * parameters and setting the new current parameter to 0. | |
618 | */ | |
619 | public finalizeParam(): void { | |
620 | this._terminal.params.push(this._terminal.currentParam); | |
621 | this._terminal.currentParam = 0; | |
622 | } | |
623 | ||
624 | /** | |
625 | * Tell the parser to skip the next character. | |
626 | */ | |
627 | public skipNextChar(): void { | |
628 | this._position++; | |
629 | } | |
630 | ||
631 | /** | |
632 | * Tell the parser to repeat parsing the current character (for example if it | |
633 | * needs parsing using a different state. | |
634 | */ | |
635 | // public repeatChar(): void { | |
636 | // this._position--; | |
637 | // } | |
638 | } |