]>
Commit | Line | Data |
---|---|---|
659fb90c DI |
1 | import { C0 } from './EscapeSequences'; |
2 | import { IInputHandler } from './Interfaces'; | |
3 | ||
a31921ae | 4 | const normalStateHandler: {[key: string]: (handler: IInputHandler) => void} = {}; |
659fb90c DI |
5 | normalStateHandler[C0.BEL] = (handler) => handler.bell(); |
6 | normalStateHandler[C0.LF] = (handler) => handler.lineFeed(); | |
7 | normalStateHandler[C0.VT] = normalStateHandler[C0.LF]; | |
8 | normalStateHandler[C0.FF] = normalStateHandler[C0.LF]; | |
9 | normalStateHandler[C0.CR] = (handler) => handler.carriageReturn(); | |
665a11ac | 10 | normalStateHandler[C0.BS] = (handler) => handler.backspace(); |
659fb90c DI |
11 | normalStateHandler[C0.HT] = (handler) => handler.tab(); |
12 | normalStateHandler[C0.SO] = (handler) => handler.shiftOut(); | |
13 | normalStateHandler[C0.SI] = (handler) => handler.shiftIn(); | |
a31921ae DI |
14 | |
15 | enum ParserState { | |
16 | NORMAL = 0, | |
17 | ESCAPED = 1, | |
18 | CSI = 2, | |
19 | OSC = 3, | |
20 | CHARSET = 4, | |
21 | DCS = 5, | |
22 | IGNORE = 6 | |
23 | } | |
24 | ||
25 | export class Parser { | |
26 | private state: ParserState; | |
27 | ||
28 | // TODO: Remove terminal when handler can do everything | |
29 | constructor( | |
30 | private _inputHandler: IInputHandler, | |
31 | private _terminal: any | |
32 | ) { | |
33 | this.state = ParserState.NORMAL; | |
34 | } | |
35 | ||
36 | public parse(data: string) { | |
37 | let l = data.length, i = 0, j, cs, ch, code, low, ch_width, row; | |
38 | ||
39 | // apply leftover surrogate high from last write | |
40 | if (this._terminal.surrogate_high) { | |
41 | data = this._terminal.surrogate_high + data; | |
42 | this._terminal.surrogate_high = ''; | |
43 | } | |
44 | ||
45 | for (; i < l; i++) { | |
46 | ch = data[i]; | |
47 | ||
48 | // FIXME: higher chars than 0xa0 are not allowed in escape sequences | |
49 | // --> maybe move to default | |
50 | code = data.charCodeAt(i); | |
51 | if (0xD800 <= code && code <= 0xDBFF) { | |
52 | // we got a surrogate high | |
53 | // get surrogate low (next 2 bytes) | |
54 | low = data.charCodeAt(i+1); | |
55 | if (isNaN(low)) { | |
56 | // end of data stream, save surrogate high | |
57 | this._terminal.surrogate_high = ch; | |
58 | continue; | |
59 | } | |
60 | code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; | |
61 | ch += data.charAt(i+1); | |
62 | } | |
63 | // surrogate low - already handled above | |
64 | if (0xDC00 <= code && code <= 0xDFFF) | |
65 | continue; | |
66 | ||
67 | if (this.state === ParserState.NORMAL) { | |
68 | if (ch in normalStateHandler) { | |
69 | normalStateHandler[ch](this._inputHandler); | |
70 | // Skip switch statement (eventually everything will be handled this way | |
71 | continue; | |
72 | } | |
73 | } | |
74 | ||
75 | switch (this.state) { | |
76 | case ParserState.NORMAL: | |
77 | switch (ch) { | |
78 | case C0.ESC: | |
79 | this.state = ParserState.ESCAPED; | |
80 | break; | |
81 | ||
82 | default: | |
83 | // ' ' | |
84 | // calculate print space | |
85 | // expensive call, therefore we save width in line buffer | |
86 | ch_width = wcwidth(code); | |
87 | ||
88 | if (ch >= ' ') { | |
89 | if (this.charset && this.charset[ch]) { | |
90 | ch = this.charset[ch]; | |
91 | } | |
92 | ||
93 | row = this.y + this.ybase; | |
94 | ||
95 | // insert combining char in last cell | |
96 | // FIXME: needs handling after cursor jumps | |
97 | if (!ch_width && this.x) { | |
98 | // dont overflow left | |
99 | if (this.lines.get(row)[this.x-1]) { | |
100 | if (!this.lines.get(row)[this.x-1][2]) { | |
101 | ||
102 | // found empty cell after fullwidth, need to go 2 cells back | |
103 | if (this.lines.get(row)[this.x-2]) | |
104 | this.lines.get(row)[this.x-2][1] += ch; | |
105 | ||
106 | } else { | |
107 | this.lines.get(row)[this.x-1][1] += ch; | |
108 | } | |
109 | this.updateRange(this.y); | |
110 | } | |
111 | break; | |
112 | } | |
113 | ||
114 | // goto next line if ch would overflow | |
115 | // TODO: needs a global min terminal width of 2 | |
116 | if (this.x+ch_width-1 >= this.cols) { | |
117 | // autowrap - DECAWM | |
118 | if (this.wraparoundMode) { | |
119 | this.x = 0; | |
120 | this.y++; | |
121 | if (this.y > this.scrollBottom) { | |
122 | this.y--; | |
123 | this.scroll(); | |
124 | } | |
125 | } else { | |
126 | this.x = this.cols-1; | |
127 | if(ch_width===2) // FIXME: check for xterm behavior | |
128 | continue; | |
129 | } | |
130 | } | |
131 | row = this.y + this.ybase; | |
132 | ||
133 | // insert mode: move characters to right | |
134 | if (this.insertMode) { | |
135 | // do this twice for a fullwidth char | |
136 | for (var moves=0; moves<ch_width; ++moves) { | |
137 | // remove last cell, if it's width is 0 | |
138 | // we have to adjust the second last cell as well | |
139 | var removed = this.lines.get(this.y + this.ybase).pop(); | |
140 | if (removed[2]===0 | |
141 | && this.lines.get(row)[this.cols-2] | |
142 | && this.lines.get(row)[this.cols-2][2]===2) | |
143 | this.lines.get(row)[this.cols-2] = [this.curAttr, ' ', 1]; | |
144 | ||
145 | // insert empty cell at cursor | |
146 | this.lines.get(row).splice(this.x, 0, [this.curAttr, ' ', 1]); | |
147 | } | |
148 | } | |
149 | ||
150 | this.lines.get(row)[this.x] = [this.curAttr, ch, ch_width]; | |
151 | this.x++; | |
152 | this.updateRange(this.y); | |
153 | ||
154 | // fullwidth char - set next cell width to zero and advance cursor | |
155 | if (ch_width===2) { | |
156 | this.lines.get(row)[this.x] = [this.curAttr, '', 0]; | |
157 | this.x++; | |
158 | } | |
159 | } | |
160 | break; | |
161 | } | |
162 | break; | |
163 | case ParserState.ESCAPED: | |
164 | switch (ch) { | |
165 | // ESC [ Control Sequence Introducer ( CSI is 0x9b). | |
166 | case '[': | |
167 | this.params = []; | |
168 | this.currentParam = 0; | |
169 | this.state = csi; | |
170 | break; | |
171 | ||
172 | // ESC ] Operating System Command ( OSC is 0x9d). | |
173 | case ']': | |
174 | this.params = []; | |
175 | this.currentParam = 0; | |
176 | this.state = osc; | |
177 | break; | |
178 | ||
179 | // ESC P Device Control String ( DCS is 0x90). | |
180 | case 'P': | |
181 | this.params = []; | |
182 | this.currentParam = 0; | |
183 | this.state = dcs; | |
184 | break; | |
185 | ||
186 | // ESC _ Application Program Command ( APC is 0x9f). | |
187 | case '_': | |
188 | this.state = ParserState.IGNORE; | |
189 | break; | |
190 | ||
191 | // ESC ^ Privacy Message ( PM is 0x9e). | |
192 | case '^': | |
193 | this.state = ParserState.IGNORE; | |
194 | break; | |
195 | ||
196 | // ESC c Full Reset (RIS). | |
197 | case 'c': | |
198 | this.reset(); | |
199 | break; | |
200 | ||
201 | // ESC E Next Line ( NEL is 0x85). | |
202 | // ESC D Index ( IND is 0x84). | |
203 | case 'E': | |
204 | this.x = 0; | |
205 | ; | |
206 | case 'D': | |
207 | this.index(); | |
208 | break; | |
209 | ||
210 | // ESC M Reverse Index ( RI is 0x8d). | |
211 | case 'M': | |
212 | this.reverseIndex(); | |
213 | break; | |
214 | ||
215 | // ESC % Select default/utf-8 character set. | |
216 | // @ = default, G = utf-8 | |
217 | case '%': | |
218 | //this.charset = null; | |
219 | this.setgLevel(0); | |
220 | this.setgCharset(0, Terminal.charsets.US); | |
221 | this.state = ParserState.NORMAL; | |
222 | i++; | |
223 | break; | |
224 | ||
225 | // ESC (,),*,+,-,. Designate G0-G2 Character Set. | |
226 | case '(': // <-- this seems to get all the attention | |
227 | case ')': | |
228 | case '*': | |
229 | case '+': | |
230 | case '-': | |
231 | case '.': | |
232 | switch (ch) { | |
233 | case '(': | |
234 | this.gcharset = 0; | |
235 | break; | |
236 | case ')': | |
237 | this.gcharset = 1; | |
238 | break; | |
239 | case '*': | |
240 | this.gcharset = 2; | |
241 | break; | |
242 | case '+': | |
243 | this.gcharset = 3; | |
244 | break; | |
245 | case '-': | |
246 | this.gcharset = 1; | |
247 | break; | |
248 | case '.': | |
249 | this.gcharset = 2; | |
250 | break; | |
251 | } | |
252 | this.state = charset; | |
253 | break; | |
254 | ||
255 | // Designate G3 Character Set (VT300). | |
256 | // A = ISO Latin-1 Supplemental. | |
257 | // Not implemented. | |
258 | case '/': | |
259 | this.gcharset = 3; | |
260 | this.state = ParserState.CHARSET; | |
261 | i--; | |
262 | break; | |
263 | ||
264 | // ESC N | |
265 | // Single Shift Select of G2 Character Set | |
266 | // ( SS2 is 0x8e). This affects next character only. | |
267 | case 'N': | |
268 | break; | |
269 | // ESC O | |
270 | // Single Shift Select of G3 Character Set | |
271 | // ( SS3 is 0x8f). This affects next character only. | |
272 | case 'O': | |
273 | break; | |
274 | // ESC n | |
275 | // Invoke the G2 Character Set as GL (LS2). | |
276 | case 'n': | |
277 | this.setgLevel(2); | |
278 | break; | |
279 | // ESC o | |
280 | // Invoke the G3 Character Set as GL (LS3). | |
281 | case 'o': | |
282 | this.setgLevel(3); | |
283 | break; | |
284 | // ESC | | |
285 | // Invoke the G3 Character Set as GR (LS3R). | |
286 | case '|': | |
287 | this.setgLevel(3); | |
288 | break; | |
289 | // ESC } | |
290 | // Invoke the G2 Character Set as GR (LS2R). | |
291 | case '}': | |
292 | this.setgLevel(2); | |
293 | break; | |
294 | // ESC ~ | |
295 | // Invoke the G1 Character Set as GR (LS1R). | |
296 | case '~': | |
297 | this.setgLevel(1); | |
298 | break; | |
299 | ||
300 | // ESC 7 Save Cursor (DECSC). | |
301 | case '7': | |
302 | this.saveCursor(); | |
303 | this.state = ParserState.NORMAL; | |
304 | break; | |
305 | ||
306 | // ESC 8 Restore Cursor (DECRC). | |
307 | case '8': | |
308 | this.restoreCursor(); | |
309 | this.state = ParserState.NORMAL; | |
310 | break; | |
311 | ||
312 | // ESC # 3 DEC line height/width | |
313 | case '#': | |
314 | this.state = ParserState.NORMAL; | |
315 | i++; | |
316 | break; | |
317 | ||
318 | // ESC H Tab Set (HTS is 0x88). | |
319 | case 'H': | |
320 | this.tabSet(); | |
321 | break; | |
322 | ||
323 | // ESC = Application Keypad (DECKPAM). | |
324 | case '=': | |
325 | this.log('Serial port requested application keypad.'); | |
326 | this.applicationKeypad = true; | |
327 | this.viewport.syncScrollArea(); | |
328 | this.state = ParserState.NORMAL; | |
329 | break; | |
330 | ||
331 | // ESC > Normal Keypad (DECKPNM). | |
332 | case '>': | |
333 | this.log('Switching back to normal keypad.'); | |
334 | this.applicationKeypad = false; | |
335 | this.viewport.syncScrollArea(); | |
336 | this.state = ParserState.NORMAL; | |
337 | break; | |
338 | ||
339 | default: | |
340 | this.state = ParserState.NORMAL; | |
341 | this.error('Unknown ESC control: %s.', ch); | |
342 | break; | |
343 | } | |
344 | break; | |
345 | ||
346 | case ParserState.CHARSET: | |
347 | switch (ch) { | |
348 | case '0': // DEC Special Character and Line Drawing Set. | |
349 | cs = Terminal.charsets.SCLD; | |
350 | break; | |
351 | case 'A': // UK | |
352 | cs = Terminal.charsets.UK; | |
353 | break; | |
354 | case 'B': // United States (USASCII). | |
355 | cs = Terminal.charsets.US; | |
356 | break; | |
357 | case '4': // Dutch | |
358 | cs = Terminal.charsets.Dutch; | |
359 | break; | |
360 | case 'C': // Finnish | |
361 | case '5': | |
362 | cs = Terminal.charsets.Finnish; | |
363 | break; | |
364 | case 'R': // French | |
365 | cs = Terminal.charsets.French; | |
366 | break; | |
367 | case 'Q': // FrenchCanadian | |
368 | cs = Terminal.charsets.FrenchCanadian; | |
369 | break; | |
370 | case 'K': // German | |
371 | cs = Terminal.charsets.German; | |
372 | break; | |
373 | case 'Y': // Italian | |
374 | cs = Terminal.charsets.Italian; | |
375 | break; | |
376 | case 'E': // NorwegianDanish | |
377 | case '6': | |
378 | cs = Terminal.charsets.NorwegianDanish; | |
379 | break; | |
380 | case 'Z': // Spanish | |
381 | cs = Terminal.charsets.Spanish; | |
382 | break; | |
383 | case 'H': // Swedish | |
384 | case '7': | |
385 | cs = Terminal.charsets.Swedish; | |
386 | break; | |
387 | case '=': // Swiss | |
388 | cs = Terminal.charsets.Swiss; | |
389 | break; | |
390 | case '/': // ISOLatin (actually /A) | |
391 | cs = Terminal.charsets.ISOLatin; | |
392 | i++; | |
393 | break; | |
394 | default: // Default | |
395 | cs = Terminal.charsets.US; | |
396 | break; | |
397 | } | |
398 | this.setgCharset(this.gcharset, cs); | |
399 | this.gcharset = null; | |
400 | this.state = ParserState.NORMAL; | |
401 | break; | |
402 | ||
403 | case ParserState.OSC: | |
404 | // OSC Ps ; Pt ST | |
405 | // OSC Ps ; Pt BEL | |
406 | // Set Text Parameters. | |
407 | if (ch === C0.ESC || ch === C0.BEL) { | |
408 | if (ch === C0.ESC) i++; | |
409 | ||
410 | this.params.push(this.currentParam); | |
411 | ||
412 | switch (this.params[0]) { | |
413 | case 0: | |
414 | case 1: | |
415 | case 2: | |
416 | if (this.params[1]) { | |
417 | this.title = this.params[1]; | |
418 | this.handleTitle(this.title); | |
419 | } | |
420 | break; | |
421 | case 3: | |
422 | // set X property | |
423 | break; | |
424 | case 4: | |
425 | case 5: | |
426 | // change dynamic colors | |
427 | break; | |
428 | case 10: | |
429 | case 11: | |
430 | case 12: | |
431 | case 13: | |
432 | case 14: | |
433 | case 15: | |
434 | case 16: | |
435 | case 17: | |
436 | case 18: | |
437 | case 19: | |
438 | // change dynamic ui colors | |
439 | break; | |
440 | case 46: | |
441 | // change log file | |
442 | break; | |
443 | case 50: | |
444 | // dynamic font | |
445 | break; | |
446 | case 51: | |
447 | // emacs shell | |
448 | break; | |
449 | case 52: | |
450 | // manipulate selection data | |
451 | break; | |
452 | case 104: | |
453 | case 105: | |
454 | case 110: | |
455 | case 111: | |
456 | case 112: | |
457 | case 113: | |
458 | case 114: | |
459 | case 115: | |
460 | case 116: | |
461 | case 117: | |
462 | case 118: | |
463 | // reset colors | |
464 | break; | |
465 | } | |
466 | ||
467 | this.params = []; | |
468 | this.currentParam = 0; | |
469 | this.state = ParserState.NORMAL; | |
470 | } else { | |
471 | if (!this.params.length) { | |
472 | if (ch >= '0' && ch <= '9') { | |
473 | this.currentParam = | |
474 | this.currentParam * 10 + ch.charCodeAt(0) - 48; | |
475 | } else if (ch === ';') { | |
476 | this.params.push(this.currentParam); | |
477 | this.currentParam = ''; | |
478 | } | |
479 | } else { | |
480 | this.currentParam += ch; | |
481 | } | |
482 | } | |
483 | break; | |
484 | ||
485 | case csi: | |
486 | // '?', '>', '!' | |
487 | if (ch === '?' || ch === '>' || ch === '!') { | |
488 | this.prefix = ch; | |
489 | break; | |
490 | } | |
491 | ||
492 | // 0 - 9 | |
493 | if (ch >= '0' && ch <= '9') { | |
494 | this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; | |
495 | break; | |
496 | } | |
497 | ||
498 | // '$', '"', ' ', '\'' | |
499 | if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') { | |
500 | this.postfix = ch; | |
501 | break; | |
502 | } | |
503 | ||
504 | this.params.push(this.currentParam); | |
505 | this.currentParam = 0; | |
506 | ||
507 | // ';' | |
508 | if (ch === ';') break; | |
509 | ||
510 | this.state = ParserState.NORMAL; | |
511 | ||
512 | switch (ch) { | |
513 | // CSI Ps A | |
514 | // Cursor Up Ps Times (default = 1) (CUU). | |
515 | case 'A': | |
516 | this.cursorUp(this.params); | |
517 | break; | |
518 | ||
519 | // CSI Ps B | |
520 | // Cursor Down Ps Times (default = 1) (CUD). | |
521 | case 'B': | |
522 | this.cursorDown(this.params); | |
523 | break; | |
524 | ||
525 | // CSI Ps C | |
526 | // Cursor Forward Ps Times (default = 1) (CUF). | |
527 | case 'C': | |
528 | this.cursorForward(this.params); | |
529 | break; | |
530 | ||
531 | // CSI Ps D | |
532 | // Cursor Backward Ps Times (default = 1) (CUB). | |
533 | case 'D': | |
534 | this.cursorBackward(this.params); | |
535 | break; | |
536 | ||
537 | // CSI Ps ; Ps H | |
538 | // Cursor Position [row;column] (default = [1,1]) (CUP). | |
539 | case 'H': | |
540 | this.cursorPos(this.params); | |
541 | break; | |
542 | ||
543 | // CSI Ps J Erase in Display (ED). | |
544 | case 'J': | |
545 | this.eraseInDisplay(this.params); | |
546 | break; | |
547 | ||
548 | // CSI Ps K Erase in Line (EL). | |
549 | case 'K': | |
550 | this.eraseInLine(this.params); | |
551 | break; | |
552 | ||
553 | // CSI Pm m Character Attributes (SGR). | |
554 | case 'm': | |
555 | if (!this.prefix) { | |
556 | this.charAttributes(this.params); | |
557 | } | |
558 | break; | |
559 | ||
560 | // CSI Ps n Device Status Report (DSR). | |
561 | case 'n': | |
562 | if (!this.prefix) { | |
563 | this.deviceStatus(this.params); | |
564 | } | |
565 | break; | |
566 | ||
567 | /** | |
568 | * Additions | |
569 | */ | |
570 | ||
571 | // CSI Ps @ | |
572 | // Insert Ps (Blank) Character(s) (default = 1) (ICH). | |
573 | case '@': | |
574 | this.insertChars(this.params); | |
575 | break; | |
576 | ||
577 | // CSI Ps E | |
578 | // Cursor Next Line Ps Times (default = 1) (CNL). | |
579 | case 'E': | |
580 | this.cursorNextLine(this.params); | |
581 | break; | |
582 | ||
583 | // CSI Ps F | |
584 | // Cursor Preceding Line Ps Times (default = 1) (CNL). | |
585 | case 'F': | |
586 | this.cursorPrecedingLine(this.params); | |
587 | break; | |
588 | ||
589 | // CSI Ps G | |
590 | // Cursor Character Absolute [column] (default = [row,1]) (CHA). | |
591 | case 'G': | |
592 | this.cursorCharAbsolute(this.params); | |
593 | break; | |
594 | ||
595 | // CSI Ps L | |
596 | // Insert Ps Line(s) (default = 1) (IL). | |
597 | case 'L': | |
598 | this.insertLines(this.params); | |
599 | break; | |
600 | ||
601 | // CSI Ps M | |
602 | // Delete Ps Line(s) (default = 1) (DL). | |
603 | case 'M': | |
604 | this.deleteLines(this.params); | |
605 | break; | |
606 | ||
607 | // CSI Ps P | |
608 | // Delete Ps Character(s) (default = 1) (DCH). | |
609 | case 'P': | |
610 | this.deleteChars(this.params); | |
611 | break; | |
612 | ||
613 | // CSI Ps X | |
614 | // Erase Ps Character(s) (default = 1) (ECH). | |
615 | case 'X': | |
616 | this.eraseChars(this.params); | |
617 | break; | |
618 | ||
619 | // CSI Pm ` Character Position Absolute | |
620 | // [column] (default = [row,1]) (HPA). | |
621 | case '`': | |
622 | this.charPosAbsolute(this.params); | |
623 | break; | |
624 | ||
625 | // 141 61 a * HPR - | |
626 | // Horizontal Position Relative | |
627 | case 'a': | |
628 | this.HPositionRelative(this.params); | |
629 | break; | |
630 | ||
631 | // CSI P s c | |
632 | // Send Device Attributes (Primary DA). | |
633 | // CSI > P s c | |
634 | // Send Device Attributes (Secondary DA) | |
635 | case 'c': | |
636 | this.sendDeviceAttributes(this.params); | |
637 | break; | |
638 | ||
639 | // CSI Pm d | |
640 | // Line Position Absolute [row] (default = [1,column]) (VPA). | |
641 | case 'd': | |
642 | this.linePosAbsolute(this.params); | |
643 | break; | |
644 | ||
645 | // 145 65 e * VPR - Vertical Position Relative | |
646 | case 'e': | |
647 | this.VPositionRelative(this.params); | |
648 | break; | |
649 | ||
650 | // CSI Ps ; Ps f | |
651 | // Horizontal and Vertical Position [row;column] (default = | |
652 | // [1,1]) (HVP). | |
653 | case 'f': | |
654 | this.HVPosition(this.params); | |
655 | break; | |
656 | ||
657 | // CSI Pm h Set Mode (SM). | |
658 | // CSI ? Pm h - mouse escape codes, cursor escape codes | |
659 | case 'h': | |
660 | this.setMode(this.params); | |
661 | break; | |
662 | ||
663 | // CSI Pm l Reset Mode (RM). | |
664 | // CSI ? Pm l | |
665 | case 'l': | |
666 | this.resetMode(this.params); | |
667 | break; | |
668 | ||
669 | // CSI Ps ; Ps r | |
670 | // Set Scrolling Region [top;bottom] (default = full size of win- | |
671 | // dow) (DECSTBM). | |
672 | // CSI ? Pm r | |
673 | case 'r': | |
674 | this.setScrollRegion(this.params); | |
675 | break; | |
676 | ||
677 | // CSI s | |
678 | // Save cursor (ANSI.SYS). | |
679 | case 's': | |
680 | this.saveCursor(this.params); | |
681 | break; | |
682 | ||
683 | // CSI u | |
684 | // Restore cursor (ANSI.SYS). | |
685 | case 'u': | |
686 | this.restoreCursor(this.params); | |
687 | break; | |
688 | ||
689 | /** | |
690 | * Lesser Used | |
691 | */ | |
692 | ||
693 | // CSI Ps I | |
694 | // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). | |
695 | case 'I': | |
696 | this.cursorForwardTab(this.params); | |
697 | break; | |
698 | ||
699 | // CSI Ps S Scroll up Ps lines (default = 1) (SU). | |
700 | case 'S': | |
701 | this.scrollUp(this.params); | |
702 | break; | |
703 | ||
704 | // CSI Ps T Scroll down Ps lines (default = 1) (SD). | |
705 | // CSI Ps ; Ps ; Ps ; Ps ; Ps T | |
706 | // CSI > Ps; Ps T | |
707 | case 'T': | |
708 | // if (this.prefix === '>') { | |
709 | // this.resetTitleModes(this.params); | |
710 | // break; | |
711 | // } | |
712 | // if (this.params.length > 2) { | |
713 | // this.initMouseTracking(this.params); | |
714 | // break; | |
715 | // } | |
716 | if (this.params.length < 2 && !this.prefix) { | |
717 | this.scrollDown(this.params); | |
718 | } | |
719 | break; | |
720 | ||
721 | // CSI Ps Z | |
722 | // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). | |
723 | case 'Z': | |
724 | this.cursorBackwardTab(this.params); | |
725 | break; | |
726 | ||
727 | // CSI Ps b Repeat the preceding graphic character Ps times (REP). | |
728 | case 'b': | |
729 | this.repeatPrecedingCharacter(this.params); | |
730 | break; | |
731 | ||
732 | // CSI Ps g Tab Clear (TBC). | |
733 | case 'g': | |
734 | this.tabClear(this.params); | |
735 | break; | |
736 | ||
737 | // CSI Pm i Media Copy (MC). | |
738 | // CSI ? Pm i | |
739 | // case 'i': | |
740 | // this.mediaCopy(this.params); | |
741 | // break; | |
742 | ||
743 | // CSI Pm m Character Attributes (SGR). | |
744 | // CSI > Ps; Ps m | |
745 | // case 'm': // duplicate | |
746 | // if (this.prefix === '>') { | |
747 | // this.setResources(this.params); | |
748 | // } else { | |
749 | // this.charAttributes(this.params); | |
750 | // } | |
751 | // break; | |
752 | ||
753 | // CSI Ps n Device Status Report (DSR). | |
754 | // CSI > Ps n | |
755 | // case 'n': // duplicate | |
756 | // if (this.prefix === '>') { | |
757 | // this.disableModifiers(this.params); | |
758 | // } else { | |
759 | // this.deviceStatus(this.params); | |
760 | // } | |
761 | // break; | |
762 | ||
763 | // CSI > Ps p Set pointer mode. | |
764 | // CSI ! p Soft terminal reset (DECSTR). | |
765 | // CSI Ps$ p | |
766 | // Request ANSI mode (DECRQM). | |
767 | // CSI ? Ps$ p | |
768 | // Request DEC private mode (DECRQM). | |
769 | // CSI Ps ; Ps " p | |
770 | case 'p': | |
771 | switch (this.prefix) { | |
772 | // case '>': | |
773 | // this.setPointerMode(this.params); | |
774 | // break; | |
775 | case '!': | |
776 | this.softReset(this.params); | |
777 | break; | |
778 | // case '?': | |
779 | // if (this.postfix === '$') { | |
780 | // this.requestPrivateMode(this.params); | |
781 | // } | |
782 | // break; | |
783 | // default: | |
784 | // if (this.postfix === '"') { | |
785 | // this.setConformanceLevel(this.params); | |
786 | // } else if (this.postfix === '$') { | |
787 | // this.requestAnsiMode(this.params); | |
788 | // } | |
789 | // break; | |
790 | } | |
791 | break; | |
792 | ||
793 | // CSI Ps q Load LEDs (DECLL). | |
794 | // CSI Ps SP q | |
795 | // CSI Ps " q | |
796 | // case 'q': | |
797 | // if (this.postfix === ' ') { | |
798 | // this.setCursorStyle(this.params); | |
799 | // break; | |
800 | // } | |
801 | // if (this.postfix === '"') { | |
802 | // this.setCharProtectionAttr(this.params); | |
803 | // break; | |
804 | // } | |
805 | // this.loadLEDs(this.params); | |
806 | // break; | |
807 | ||
808 | // CSI Ps ; Ps r | |
809 | // Set Scrolling Region [top;bottom] (default = full size of win- | |
810 | // dow) (DECSTBM). | |
811 | // CSI ? Pm r | |
812 | // CSI Pt; Pl; Pb; Pr; Ps$ r | |
813 | // case 'r': // duplicate | |
814 | // if (this.prefix === '?') { | |
815 | // this.restorePrivateValues(this.params); | |
816 | // } else if (this.postfix === '$') { | |
817 | // this.setAttrInRectangle(this.params); | |
818 | // } else { | |
819 | // this.setScrollRegion(this.params); | |
820 | // } | |
821 | // break; | |
822 | ||
823 | // CSI s Save cursor (ANSI.SYS). | |
824 | // CSI ? Pm s | |
825 | // case 's': // duplicate | |
826 | // if (this.prefix === '?') { | |
827 | // this.savePrivateValues(this.params); | |
828 | // } else { | |
829 | // this.saveCursor(this.params); | |
830 | // } | |
831 | // break; | |
832 | ||
833 | // CSI Ps ; Ps ; Ps t | |
834 | // CSI Pt; Pl; Pb; Pr; Ps$ t | |
835 | // CSI > Ps; Ps t | |
836 | // CSI Ps SP t | |
837 | // case 't': | |
838 | // if (this.postfix === '$') { | |
839 | // this.reverseAttrInRectangle(this.params); | |
840 | // } else if (this.postfix === ' ') { | |
841 | // this.setWarningBellVolume(this.params); | |
842 | // } else { | |
843 | // if (this.prefix === '>') { | |
844 | // this.setTitleModeFeature(this.params); | |
845 | // } else { | |
846 | // this.manipulateWindow(this.params); | |
847 | // } | |
848 | // } | |
849 | // break; | |
850 | ||
851 | // CSI u Restore cursor (ANSI.SYS). | |
852 | // CSI Ps SP u | |
853 | // case 'u': // duplicate | |
854 | // if (this.postfix === ' ') { | |
855 | // this.setMarginBellVolume(this.params); | |
856 | // } else { | |
857 | // this.restoreCursor(this.params); | |
858 | // } | |
859 | // break; | |
860 | ||
861 | // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v | |
862 | // case 'v': | |
863 | // if (this.postfix === '$') { | |
864 | // this.copyRectagle(this.params); | |
865 | // } | |
866 | // break; | |
867 | ||
868 | // CSI Pt ; Pl ; Pb ; Pr ' w | |
869 | // case 'w': | |
870 | // if (this.postfix === '\'') { | |
871 | // this.enableFilterRectangle(this.params); | |
872 | // } | |
873 | // break; | |
874 | ||
875 | // CSI Ps x Request Terminal Parameters (DECREQTPARM). | |
876 | // CSI Ps x Select Attribute Change Extent (DECSACE). | |
877 | // CSI Pc; Pt; Pl; Pb; Pr$ x | |
878 | // case 'x': | |
879 | // if (this.postfix === '$') { | |
880 | // this.fillRectangle(this.params); | |
881 | // } else { | |
882 | // this.requestParameters(this.params); | |
883 | // //this.__(this.params); | |
884 | // } | |
885 | // break; | |
886 | ||
887 | // CSI Ps ; Pu ' z | |
888 | // CSI Pt; Pl; Pb; Pr$ z | |
889 | // case 'z': | |
890 | // if (this.postfix === '\'') { | |
891 | // this.enableLocatorReporting(this.params); | |
892 | // } else if (this.postfix === '$') { | |
893 | // this.eraseRectangle(this.params); | |
894 | // } | |
895 | // break; | |
896 | ||
897 | // CSI Pm ' { | |
898 | // CSI Pt; Pl; Pb; Pr$ { | |
899 | // case '{': | |
900 | // if (this.postfix === '\'') { | |
901 | // this.setLocatorEvents(this.params); | |
902 | // } else if (this.postfix === '$') { | |
903 | // this.selectiveEraseRectangle(this.params); | |
904 | // } | |
905 | // break; | |
906 | ||
907 | // CSI Ps ' | | |
908 | // case '|': | |
909 | // if (this.postfix === '\'') { | |
910 | // this.requestLocatorPosition(this.params); | |
911 | // } | |
912 | // break; | |
913 | ||
914 | // CSI P m SP } | |
915 | // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. | |
916 | // case '}': | |
917 | // if (this.postfix === ' ') { | |
918 | // this.insertColumns(this.params); | |
919 | // } | |
920 | // break; | |
921 | ||
922 | // CSI P m SP ~ | |
923 | // Delete P s Column(s) (default = 1) (DECDC), VT420 and up | |
924 | // case '~': | |
925 | // if (this.postfix === ' ') { | |
926 | // this.deleteColumns(this.params); | |
927 | // } | |
928 | // break; | |
929 | ||
930 | default: | |
931 | this.error('Unknown CSI code: %s.', ch); | |
932 | break; | |
933 | } | |
934 | ||
935 | this.prefix = ''; | |
936 | this.postfix = ''; | |
937 | break; | |
938 | ||
939 | case ParserState.DCS: | |
940 | if (ch === C0.ESC || ch === C0.BEL) { | |
941 | if (ch === C0.ESC) i++; | |
942 | ||
943 | switch (this.prefix) { | |
944 | // User-Defined Keys (DECUDK). | |
945 | case '': | |
946 | break; | |
947 | ||
948 | // Request Status String (DECRQSS). | |
949 | // test: echo -e '\eP$q"p\e\\' | |
950 | case '$q': | |
951 | var pt = this.currentParam | |
952 | , valid = false; | |
953 | ||
954 | switch (pt) { | |
955 | // DECSCA | |
956 | case '"q': | |
957 | pt = '0"q'; | |
958 | break; | |
959 | ||
960 | // DECSCL | |
961 | case '"p': | |
962 | pt = '61"p'; | |
963 | break; | |
964 | ||
965 | // DECSTBM | |
966 | case 'r': | |
967 | pt = '' | |
968 | + (this.scrollTop + 1) | |
969 | + ';' | |
970 | + (this.scrollBottom + 1) | |
971 | + 'r'; | |
972 | break; | |
973 | ||
974 | // SGR | |
975 | case 'm': | |
976 | pt = '0m'; | |
977 | break; | |
978 | ||
979 | default: | |
980 | this.error('Unknown DCS Pt: %s.', pt); | |
981 | pt = ''; | |
982 | break; | |
983 | } | |
984 | ||
985 | this.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\'); | |
986 | break; | |
987 | ||
988 | // Set Termcap/Terminfo Data (xterm, experimental). | |
989 | case '+p': | |
990 | break; | |
991 | ||
992 | // Request Termcap/Terminfo String (xterm, experimental) | |
993 | // Regular xterm does not even respond to this sequence. | |
994 | // This can cause a small glitch in vim. | |
995 | // test: echo -ne '\eP+q6b64\e\\' | |
996 | case '+q': | |
997 | var pt = this.currentParam | |
998 | , valid = false; | |
999 | ||
1000 | this.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\'); | |
1001 | break; | |
1002 | ||
1003 | default: | |
1004 | this.error('Unknown DCS prefix: %s.', this.prefix); | |
1005 | break; | |
1006 | } | |
1007 | ||
1008 | this.currentParam = 0; | |
1009 | this.prefix = ''; | |
1010 | this.state = ParserState.NORMAL; | |
1011 | } else if (!this.currentParam) { | |
1012 | if (!this.prefix && ch !== '$' && ch !== '+') { | |
1013 | this.currentParam = ch; | |
1014 | } else if (this.prefix.length === 2) { | |
1015 | this.currentParam = ch; | |
1016 | } else { | |
1017 | this.prefix += ch; | |
1018 | } | |
1019 | } else { | |
1020 | this.currentParam += ch; | |
1021 | } | |
1022 | break; | |
1023 | ||
1024 | case ParserState.IGNORE: | |
1025 | // For PM and APC. | |
1026 | if (ch === C0.ESC || ch === C0.BEL) { | |
1027 | if (ch === C0.ESC) i++; | |
1028 | this.state = ParserState.NORMAL; | |
1029 | } | |
1030 | break; | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | } |