]>
Commit | Line | Data |
---|---|---|
659fb90c DI |
1 | import { C0 } from './EscapeSequences'; |
2 | import { IInputHandler } from './Interfaces'; | |
1848fff4 | 3 | import { CHARSETS } from './Charsets'; |
659fb90c | 4 | |
fa3484cd DI |
5 | const normalStateHandler: {[key: string]: (parser: Parser, handler: IInputHandler) => void} = {}; |
6 | normalStateHandler[C0.BEL] = (parser, handler) => handler.bell(); | |
7 | normalStateHandler[C0.LF] = (parser, handler) => handler.lineFeed(); | |
659fb90c DI |
8 | normalStateHandler[C0.VT] = normalStateHandler[C0.LF]; |
9 | normalStateHandler[C0.FF] = normalStateHandler[C0.LF]; | |
fa3484cd DI |
10 | normalStateHandler[C0.CR] = (parser, handler) => handler.carriageReturn(); |
11 | normalStateHandler[C0.BS] = (parser, handler) => handler.backspace(); | |
12 | normalStateHandler[C0.HT] = (parser, handler) => handler.tab(); | |
13 | normalStateHandler[C0.SO] = (parser, handler) => handler.shiftOut(); | |
14 | normalStateHandler[C0.SI] = (parser, handler) => handler.shiftIn(); | |
15 | normalStateHandler[C0.ESC] = (parser, handler) => parser.setState(ParserState.ESCAPED); | |
672dc1a2 | 16 | |
ecd7e69f DI |
17 | // TODO: Remove terminal when parser owns params and currentParam |
18 | const escapedStateHandler: {[key: string]: (parser: Parser, terminal: any) => void} = {}; | |
19 | escapedStateHandler['['] = (parser, terminal) => { | |
20 | // ESC [ Control Sequence Introducer (CSI is 0x9b) | |
21 | terminal.params = []; | |
22 | terminal.currentParam = 0; | |
23 | parser.setState(ParserState.CSI_PARAM); | |
24 | }; | |
25 | escapedStateHandler[']'] = (parser, terminal) => { | |
26 | // ESC ] Operating System Command (OSC is 0x9d) | |
27 | terminal.params = []; | |
28 | terminal.currentParam = 0; | |
29 | parser.setState(ParserState.OSC); | |
30 | }; | |
31 | escapedStateHandler['P'] = (parser, terminal) => { | |
32 | // ESC P Device Control String (DCS is 0x90) | |
33 | terminal.params = []; | |
34 | terminal.currentParam = 0; | |
35 | parser.setState(ParserState.DCS); | |
36 | }; | |
37 | escapedStateHandler['_'] = (parser, terminal) => { | |
38 | // ESC _ Application Program Command ( APC is 0x9f). | |
39 | parser.setState(ParserState.IGNORE); | |
4448ebe3 | 40 | }; |
ecd7e69f DI |
41 | escapedStateHandler['^'] = (parser, terminal) => { |
42 | // ESC ^ Privacy Message ( PM is 0x9e). | |
43 | parser.setState(ParserState.IGNORE); | |
4448ebe3 | 44 | }; |
ecd7e69f DI |
45 | escapedStateHandler['c'] = (parser, terminal) => { |
46 | // ESC c Full Reset (RIS). | |
47 | terminal.reset(); | |
4448ebe3 | 48 | }; |
ecd7e69f DI |
49 | escapedStateHandler['E'] = (parser, terminal) => { |
50 | // ESC E Next Line ( NEL is 0x85). | |
51 | terminal.x = 0; | |
52 | terminal.index(); | |
53 | parser.setState(ParserState.NORMAL); | |
4448ebe3 | 54 | }; |
ecd7e69f DI |
55 | escapedStateHandler['D'] = (parser, terminal) => { |
56 | // ESC D Index ( IND is 0x84). | |
57 | terminal.index(); | |
58 | parser.setState(ParserState.NORMAL); | |
59 | }; | |
60 | escapedStateHandler['M'] = (parser, terminal) => { | |
61 | // ESC M Reverse Index ( RI is 0x8d). | |
62 | terminal.reverseIndex(); | |
63 | parser.setState(ParserState.NORMAL); | |
64 | }; | |
65 | escapedStateHandler['%'] = (parser, terminal) => { | |
66 | // ESC % Select default/utf-8 character set. | |
67 | // @ = default, G = utf-8 | |
68 | terminal.setgLevel(0); | |
69 | terminal.setgCharset(0, CHARSETS.US); | |
70 | parser.setState(ParserState.NORMAL); | |
71 | parser.skipNextChar(); | |
72 | }; | |
73 | ||
7572dd5f DI |
74 | const csiParamStateHandler: {[key: string]: (parser: Parser) => void} = {}; |
75 | csiParamStateHandler['?'] = (parser) => parser.setPrefix('?'); | |
76 | csiParamStateHandler['>'] = (parser) => parser.setPrefix('>'); | |
77 | csiParamStateHandler['!'] = (parser) => parser.setPrefix('!'); | |
78 | csiParamStateHandler['0'] = (parser) => parser.setParam(parser.getParam() * 10); | |
79 | csiParamStateHandler['1'] = (parser) => parser.setParam(parser.getParam() * 10 + 1); | |
80 | csiParamStateHandler['2'] = (parser) => parser.setParam(parser.getParam() * 10 + 2); | |
81 | csiParamStateHandler['3'] = (parser) => parser.setParam(parser.getParam() * 10 + 3); | |
82 | csiParamStateHandler['4'] = (parser) => parser.setParam(parser.getParam() * 10 + 4); | |
83 | csiParamStateHandler['5'] = (parser) => parser.setParam(parser.getParam() * 10 + 5); | |
84 | csiParamStateHandler['6'] = (parser) => parser.setParam(parser.getParam() * 10 + 6); | |
85 | csiParamStateHandler['7'] = (parser) => parser.setParam(parser.getParam() * 10 + 7); | |
86 | csiParamStateHandler['8'] = (parser) => parser.setParam(parser.getParam() * 10 + 8); | |
87 | csiParamStateHandler['9'] = (parser) => parser.setParam(parser.getParam() * 10 + 9); | |
88 | csiParamStateHandler['$'] = (parser) => parser.setPostfix('$'); | |
89 | csiParamStateHandler['"'] = (parser) => parser.setPostfix('"'); | |
90 | csiParamStateHandler[' '] = (parser) => parser.setPostfix(' '); | |
91 | csiParamStateHandler['\''] = (parser) => parser.setPostfix('\''); | |
c7fa2d55 | 92 | csiParamStateHandler[';'] = (parser) => parser.finalizeParam(); |
9942477b | 93 | |
f4846aa1 DI |
94 | const csiStateHandler: {[key: string]: (handler: IInputHandler, params: number[], prefix: string) => void} = {}; |
95 | csiStateHandler['@'] = (handler, params, prefix) => handler.insertChars(params); | |
96 | csiStateHandler['A'] = (handler, params, prefix) => handler.cursorUp(params); | |
97 | csiStateHandler['B'] = (handler, params, prefix) => handler.cursorDown(params); | |
98 | csiStateHandler['C'] = (handler, params, prefix) => handler.cursorForward(params); | |
99 | csiStateHandler['D'] = (handler, params, prefix) => handler.cursorBackward(params); | |
100 | csiStateHandler['E'] = (handler, params, prefix) => handler.cursorNextLine(params); | |
101 | csiStateHandler['F'] = (handler, params, prefix) => handler.cursorPrecedingLine(params); | |
102 | csiStateHandler['G'] = (handler, params, prefix) => handler.cursorCharAbsolute(params); | |
103 | csiStateHandler['H'] = (handler, params, prefix) => handler.cursorPosition(params); | |
104 | csiStateHandler['I'] = (handler, params, prefix) => handler.cursorForwardTab(params); | |
105 | csiStateHandler['J'] = (handler, params, prefix) => handler.eraseInDisplay(params); | |
106 | csiStateHandler['K'] = (handler, params, prefix) => handler.eraseInLine(params); | |
107 | csiStateHandler['L'] = (handler, params, prefix) => handler.insertLines(params); | |
108 | csiStateHandler['M'] = (handler, params, prefix) => handler.deleteLines(params); | |
109 | csiStateHandler['P'] = (handler, params, prefix) => handler.deleteChars(params); | |
110 | csiStateHandler['S'] = (handler, params, prefix) => handler.scrollUp(params); | |
111 | csiStateHandler['T'] = (handler, params, prefix) => { | |
112 | if (params.length < 2 && !prefix) { | |
113 | handler.scrollDown(params); | |
114 | } | |
115 | }; | |
116 | csiStateHandler['X'] = (handler, params, prefix) => handler.eraseChars(params); | |
117 | csiStateHandler['Z'] = (handler, params, prefix) => handler.cursorBackwardTab(params); | |
118 | csiStateHandler['`'] = (handler, params, prefix) => handler.charPosAbsolute(params); | |
119 | csiStateHandler['a'] = (handler, params, prefix) => handler.HPositionRelative(params); | |
120 | csiStateHandler['b'] = (handler, params, prefix) => handler.repeatPrecedingCharacter(params); | |
121 | csiStateHandler['c'] = (handler, params, prefix) => handler.sendDeviceAttributes(params); | |
122 | csiStateHandler['d'] = (handler, params, prefix) => handler.linePosAbsolute(params); | |
123 | csiStateHandler['e'] = (handler, params, prefix) => handler.VPositionRelative(params); | |
124 | csiStateHandler['f'] = (handler, params, prefix) => handler.HVPosition(params); | |
125 | csiStateHandler['g'] = (handler, params, prefix) => handler.tabClear(params); | |
126 | csiStateHandler['h'] = (handler, params, prefix) => handler.setMode(params); | |
127 | csiStateHandler['l'] = (handler, params, prefix) => handler.resetMode(params); | |
128 | csiStateHandler['m'] = (handler, params, prefix) => handler.charAttributes(params); | |
129 | csiStateHandler['n'] = (handler, params, prefix) => handler.deviceStatus(params); | |
130 | csiStateHandler['p'] = (handler, params, prefix) => { | |
131 | switch (prefix) { | |
132 | case '!': handler.softReset(params); break; | |
133 | } | |
134 | }; | |
9b662080 DI |
135 | csiStateHandler['r'] = (handler, params) => handler.setScrollRegion(params); |
136 | csiStateHandler['s'] = (handler, params) => handler.saveCursor(params); | |
137 | csiStateHandler['u'] = (handler, params) => handler.restoreCursor(params); | |
a31921ae | 138 | |
4448ebe3 DI |
139 | // TODO: Many codes/charsets appear to not be supported |
140 | // See: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html | |
141 | const charsetMap = { | |
142 | '0': CHARSETS.SCLD, | |
143 | 'A': CHARSETS.UK, // United Kingdom | |
144 | 'B': CHARSETS.US, // United States (USASCII) | |
145 | '4': CHARSETS.Dutch, | |
146 | 'C': CHARSETS.Finnish, | |
147 | '5': CHARSETS.Finnish, | |
148 | 'f': CHARSETS.French, | |
149 | 'Q': CHARSETS.FrenchCanadian, | |
150 | 'K': CHARSETS.German, | |
151 | 'Y': CHARSETS.Italian, | |
152 | 'E': CHARSETS.NorwegianDanish, | |
153 | '6': CHARSETS.NorwegianDanish, | |
154 | 'Z': CHARSETS.Spanish, | |
155 | 'H': CHARSETS.Swedish, | |
156 | '7': CHARSETS.Swedish, | |
157 | '=': CHARSETS.Swiss, | |
158 | '/': CHARSETS.ISOLatin // ISOLatin is actually /A | |
159 | }; | |
160 | ||
a31921ae DI |
161 | enum ParserState { |
162 | NORMAL = 0, | |
163 | ESCAPED = 1, | |
c7fa2d55 DI |
164 | CSI_PARAM = 2, |
165 | CSI = 3, | |
166 | OSC = 4, | |
167 | CHARSET = 5, | |
168 | DCS = 6, | |
169 | IGNORE = 7 | |
a31921ae DI |
170 | } |
171 | ||
172 | export class Parser { | |
ecd7e69f DI |
173 | private _state: ParserState; |
174 | private _position: number; | |
a31921ae DI |
175 | |
176 | // TODO: Remove terminal when handler can do everything | |
177 | constructor( | |
178 | private _inputHandler: IInputHandler, | |
179 | private _terminal: any | |
180 | ) { | |
ecd7e69f | 181 | this._state = ParserState.NORMAL; |
a31921ae DI |
182 | } |
183 | ||
184 | public parse(data: string) { | |
ecd7e69f | 185 | let l = data.length, j, cs, ch, code, low; |
a31921ae | 186 | |
ecd7e69f | 187 | this._position = 0; |
a31921ae DI |
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 | ||
ecd7e69f DI |
194 | for (; this._position < l; this._position++) { |
195 | ch = data[this._position]; | |
a31921ae DI |
196 | |
197 | // FIXME: higher chars than 0xa0 are not allowed in escape sequences | |
198 | // --> maybe move to default | |
ecd7e69f | 199 | code = data.charCodeAt(this._position); |
a31921ae DI |
200 | if (0xD800 <= code && code <= 0xDBFF) { |
201 | // we got a surrogate high | |
202 | // get surrogate low (next 2 bytes) | |
ecd7e69f | 203 | low = data.charCodeAt(this._position + 1); |
a31921ae DI |
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; | |
ecd7e69f | 210 | ch += data.charAt(this._position + 1); |
a31921ae DI |
211 | } |
212 | // surrogate low - already handled above | |
213 | if (0xDC00 <= code && code <= 0xDFFF) | |
214 | continue; | |
215 | ||
ecd7e69f | 216 | switch (this._state) { |
a31921ae | 217 | case ParserState.NORMAL: |
fa3484cd DI |
218 | if (ch in normalStateHandler) { |
219 | normalStateHandler[ch](this, this._inputHandler); | |
220 | } else { | |
221 | this._inputHandler.addChar(ch, code); | |
a31921ae DI |
222 | } |
223 | break; | |
224 | case ParserState.ESCAPED: | |
ecd7e69f DI |
225 | if (ch in escapedStateHandler) { |
226 | escapedStateHandler[ch](this, this._terminal); | |
227 | // Skip switch as it was just handled | |
228 | break; | |
229 | } | |
a31921ae | 230 | switch (ch) { |
a31921ae DI |
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 '(': | |
991b2843 | 241 | this._terminal.gcharset = 0; |
a31921ae DI |
242 | break; |
243 | case ')': | |
991b2843 | 244 | this._terminal.gcharset = 1; |
a31921ae DI |
245 | break; |
246 | case '*': | |
991b2843 | 247 | this._terminal.gcharset = 2; |
a31921ae DI |
248 | break; |
249 | case '+': | |
991b2843 | 250 | this._terminal.gcharset = 3; |
a31921ae DI |
251 | break; |
252 | case '-': | |
991b2843 | 253 | this._terminal.gcharset = 1; |
a31921ae DI |
254 | break; |
255 | case '.': | |
991b2843 | 256 | this._terminal.gcharset = 2; |
a31921ae DI |
257 | break; |
258 | } | |
ecd7e69f | 259 | this._state = ParserState.CHARSET; |
a31921ae DI |
260 | break; |
261 | ||
262 | // Designate G3 Character Set (VT300). | |
263 | // A = ISO Latin-1 Supplemental. | |
264 | // Not implemented. | |
265 | case '/': | |
991b2843 | 266 | this._terminal.gcharset = 3; |
ecd7e69f DI |
267 | this._state = ParserState.CHARSET; |
268 | this._position--; | |
a31921ae DI |
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': | |
991b2843 | 284 | this._terminal.setgLevel(2); |
a31921ae DI |
285 | break; |
286 | // ESC o | |
287 | // Invoke the G3 Character Set as GL (LS3). | |
288 | case 'o': | |
991b2843 | 289 | this._terminal.setgLevel(3); |
a31921ae DI |
290 | break; |
291 | // ESC | | |
292 | // Invoke the G3 Character Set as GR (LS3R). | |
293 | case '|': | |
991b2843 | 294 | this._terminal.setgLevel(3); |
a31921ae DI |
295 | break; |
296 | // ESC } | |
297 | // Invoke the G2 Character Set as GR (LS2R). | |
298 | case '}': | |
991b2843 | 299 | this._terminal.setgLevel(2); |
a31921ae DI |
300 | break; |
301 | // ESC ~ | |
302 | // Invoke the G1 Character Set as GR (LS1R). | |
303 | case '~': | |
991b2843 | 304 | this._terminal.setgLevel(1); |
a31921ae DI |
305 | break; |
306 | ||
307 | // ESC 7 Save Cursor (DECSC). | |
308 | case '7': | |
f4846aa1 | 309 | this._inputHandler.saveCursor(); |
ecd7e69f | 310 | this._state = ParserState.NORMAL; |
a31921ae DI |
311 | break; |
312 | ||
313 | // ESC 8 Restore Cursor (DECRC). | |
314 | case '8': | |
f4846aa1 | 315 | this._inputHandler.restoreCursor(); |
ecd7e69f | 316 | this._state = ParserState.NORMAL; |
a31921ae DI |
317 | break; |
318 | ||
319 | // ESC # 3 DEC line height/width | |
320 | case '#': | |
ecd7e69f DI |
321 | this._state = ParserState.NORMAL; |
322 | this._position++; | |
a31921ae DI |
323 | break; |
324 | ||
325 | // ESC H Tab Set (HTS is 0x88). | |
326 | case 'H': | |
991b2843 | 327 | this._terminal.tabSet(); |
ecd7e69f | 328 | this._state = ParserState.NORMAL; |
a31921ae DI |
329 | break; |
330 | ||
331 | // ESC = Application Keypad (DECKPAM). | |
332 | case '=': | |
991b2843 DI |
333 | this._terminal.log('Serial port requested application keypad.'); |
334 | this._terminal.applicationKeypad = true; | |
335 | this._terminal.viewport.syncScrollArea(); | |
ecd7e69f | 336 | this._state = ParserState.NORMAL; |
a31921ae DI |
337 | break; |
338 | ||
339 | // ESC > Normal Keypad (DECKPNM). | |
340 | case '>': | |
991b2843 DI |
341 | this._terminal.log('Switching back to normal keypad.'); |
342 | this._terminal.applicationKeypad = false; | |
343 | this._terminal.viewport.syncScrollArea(); | |
ecd7e69f | 344 | this._state = ParserState.NORMAL; |
a31921ae DI |
345 | break; |
346 | ||
347 | default: | |
ecd7e69f | 348 | this._state = ParserState.NORMAL; |
991b2843 | 349 | this._terminal.error('Unknown ESC control: %s.', ch); |
a31921ae DI |
350 | break; |
351 | } | |
352 | break; | |
353 | ||
354 | case ParserState.CHARSET: | |
4448ebe3 DI |
355 | if (ch in charsetMap) { |
356 | cs = charsetMap[ch]; | |
357 | if (ch === '/') { // ISOLatin is actually /A | |
358 | this.skipNextChar(); | |
359 | } | |
360 | } else { | |
361 | cs = CHARSETS.US; // Default | |
a31921ae | 362 | } |
991b2843 DI |
363 | this._terminal.setgCharset(this._terminal.gcharset, cs); |
364 | this._terminal.gcharset = null; | |
ecd7e69f | 365 | this._state = ParserState.NORMAL; |
a31921ae DI |
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) { | |
ecd7e69f | 373 | if (ch === C0.ESC) this._position++; |
a31921ae | 374 | |
991b2843 | 375 | this._terminal.params.push(this._terminal.currentParam); |
a31921ae | 376 | |
991b2843 | 377 | switch (this._terminal.params[0]) { |
a31921ae DI |
378 | case 0: |
379 | case 1: | |
380 | case 2: | |
991b2843 DI |
381 | if (this._terminal.params[1]) { |
382 | this._terminal.title = this._terminal.params[1]; | |
383 | this._terminal.handleTitle(this._terminal.title); | |
a31921ae DI |
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 | ||
991b2843 DI |
432 | this._terminal.params = []; |
433 | this._terminal.currentParam = 0; | |
ecd7e69f | 434 | this._state = ParserState.NORMAL; |
a31921ae | 435 | } else { |
991b2843 | 436 | if (!this._terminal.params.length) { |
a31921ae | 437 | if (ch >= '0' && ch <= '9') { |
991b2843 DI |
438 | this._terminal.currentParam = |
439 | this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48; | |
a31921ae | 440 | } else if (ch === ';') { |
991b2843 DI |
441 | this._terminal.params.push(this._terminal.currentParam); |
442 | this._terminal.currentParam = ''; | |
a31921ae DI |
443 | } |
444 | } else { | |
991b2843 | 445 | this._terminal.currentParam += ch; |
a31921ae DI |
446 | } |
447 | } | |
448 | break; | |
449 | ||
c7fa2d55 | 450 | case ParserState.CSI_PARAM: |
9942477b | 451 | if (ch in csiParamStateHandler) { |
7572dd5f | 452 | csiParamStateHandler[ch](this); |
a31921ae DI |
453 | break; |
454 | } | |
c7fa2d55 DI |
455 | this.finalizeParam(); |
456 | // Fall through the CSI as this character should be the CSI code. | |
ecd7e69f | 457 | this._state = ParserState.CSI; |
a31921ae | 458 | |
c7fa2d55 | 459 | case ParserState.CSI: |
9942477b | 460 | if (ch in csiStateHandler) { |
f4846aa1 DI |
461 | csiStateHandler[ch](this._inputHandler, this._terminal.params, this._terminal.prefix); |
462 | } else { | |
463 | this._terminal.error('Unknown CSI code: %s.', ch); | |
a31921ae DI |
464 | } |
465 | ||
ecd7e69f | 466 | this._state = ParserState.NORMAL; |
991b2843 DI |
467 | this._terminal.prefix = ''; |
468 | this._terminal.postfix = ''; | |
a31921ae DI |
469 | break; |
470 | ||
471 | case ParserState.DCS: | |
472 | if (ch === C0.ESC || ch === C0.BEL) { | |
ecd7e69f | 473 | if (ch === C0.ESC) this._position++; |
a31921ae | 474 | |
991b2843 | 475 | switch (this._terminal.prefix) { |
a31921ae DI |
476 | // User-Defined Keys (DECUDK). |
477 | case '': | |
478 | break; | |
479 | ||
480 | // Request Status String (DECRQSS). | |
481 | // test: echo -e '\eP$q"p\e\\' | |
482 | case '$q': | |
991b2843 | 483 | let pt = this._terminal.currentParam |
a31921ae DI |
484 | , valid = false; |
485 | ||
486 | switch (pt) { | |
487 | // DECSCA | |
488 | case '"q': | |
489 | pt = '0"q'; | |
490 | break; | |
491 | ||
492 | // DECSCL | |
493 | case '"p': | |
494 | pt = '61"p'; | |
495 | break; | |
496 | ||
497 | // DECSTBM | |
498 | case 'r': | |
499 | pt = '' | |
991b2843 | 500 | + (this._terminal.scrollTop + 1) |
a31921ae | 501 | + ';' |
991b2843 | 502 | + (this._terminal.scrollBottom + 1) |
a31921ae DI |
503 | + 'r'; |
504 | break; | |
505 | ||
506 | // SGR | |
507 | case 'm': | |
508 | pt = '0m'; | |
509 | break; | |
510 | ||
511 | default: | |
991b2843 | 512 | this._terminal.error('Unknown DCS Pt: %s.', pt); |
a31921ae DI |
513 | pt = ''; |
514 | break; | |
515 | } | |
516 | ||
991b2843 | 517 | this._terminal.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\'); |
a31921ae DI |
518 | break; |
519 | ||
520 | // Set Termcap/Terminfo Data (xterm, experimental). | |
521 | case '+p': | |
522 | break; | |
523 | ||
524 | // Request Termcap/Terminfo String (xterm, experimental) | |
525 | // Regular xterm does not even respond to this sequence. | |
526 | // This can cause a small glitch in vim. | |
527 | // test: echo -ne '\eP+q6b64\e\\' | |
528 | case '+q': | |
991b2843 DI |
529 | // TODO: Don't declare pt twice |
530 | /*let*/ pt = this._terminal.currentParam | |
a31921ae DI |
531 | , valid = false; |
532 | ||
991b2843 | 533 | this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\'); |
a31921ae DI |
534 | break; |
535 | ||
536 | default: | |
991b2843 | 537 | this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix); |
a31921ae DI |
538 | break; |
539 | } | |
540 | ||
991b2843 DI |
541 | this._terminal.currentParam = 0; |
542 | this._terminal.prefix = ''; | |
ecd7e69f | 543 | this._state = ParserState.NORMAL; |
991b2843 DI |
544 | } else if (!this._terminal.currentParam) { |
545 | if (!this._terminal.prefix && ch !== '$' && ch !== '+') { | |
546 | this._terminal.currentParam = ch; | |
547 | } else if (this._terminal.prefix.length === 2) { | |
548 | this._terminal.currentParam = ch; | |
a31921ae | 549 | } else { |
991b2843 | 550 | this._terminal.prefix += ch; |
a31921ae DI |
551 | } |
552 | } else { | |
991b2843 | 553 | this._terminal.currentParam += ch; |
a31921ae DI |
554 | } |
555 | break; | |
556 | ||
557 | case ParserState.IGNORE: | |
558 | // For PM and APC. | |
559 | if (ch === C0.ESC || ch === C0.BEL) { | |
ecd7e69f DI |
560 | if (ch === C0.ESC) this._position++; |
561 | this._state = ParserState.NORMAL; | |
a31921ae DI |
562 | } |
563 | break; | |
564 | } | |
565 | } | |
566 | } | |
672dc1a2 | 567 | |
fa3484cd | 568 | public setState(state: ParserState): void { |
ecd7e69f | 569 | this._state = state; |
fa3484cd DI |
570 | } |
571 | ||
635a4d76 | 572 | public setPrefix(prefix: string): void { |
672dc1a2 DI |
573 | this._terminal.prefix = prefix; |
574 | } | |
575 | ||
576 | public setParam(param: number) { | |
577 | this._terminal.currentParam = param; | |
578 | } | |
579 | ||
580 | public getParam(): number { | |
581 | return this._terminal.currentParam; | |
582 | } | |
635a4d76 | 583 | |
c7fa2d55 DI |
584 | public finalizeParam(): void { |
585 | this._terminal.params.push(this._terminal.currentParam); | |
586 | this._terminal.currentParam = 0; | |
587 | } | |
588 | ||
635a4d76 DI |
589 | public setPostfix(postfix: string): void { |
590 | this._terminal.postfix = postfix; | |
591 | } | |
ecd7e69f DI |
592 | |
593 | public skipNextChar(): void { | |
594 | this._position++; | |
595 | } | |
596 | ||
597 | // public repeatChar(): void { | |
598 | // this._position--; | |
599 | // } | |
a31921ae | 600 | } |