]>
Commit | Line | Data |
---|---|---|
659fb90c DI |
1 | import { C0 } from './EscapeSequences'; |
2 | import { IInputHandler } from './Interfaces'; | |
1848fff4 | 3 | import { CHARSETS } from './Charsets'; |
659fb90c | 4 | |
a31921ae | 5 | const normalStateHandler: {[key: string]: (handler: IInputHandler) => void} = {}; |
659fb90c DI |
6 | normalStateHandler[C0.BEL] = (handler) => handler.bell(); |
7 | normalStateHandler[C0.LF] = (handler) => handler.lineFeed(); | |
8 | normalStateHandler[C0.VT] = normalStateHandler[C0.LF]; | |
9 | normalStateHandler[C0.FF] = normalStateHandler[C0.LF]; | |
10 | normalStateHandler[C0.CR] = (handler) => handler.carriageReturn(); | |
665a11ac | 11 | normalStateHandler[C0.BS] = (handler) => handler.backspace(); |
659fb90c DI |
12 | normalStateHandler[C0.HT] = (handler) => handler.tab(); |
13 | normalStateHandler[C0.SO] = (handler) => handler.shiftOut(); | |
14 | normalStateHandler[C0.SI] = (handler) => handler.shiftIn(); | |
672dc1a2 DI |
15 | // TODO: Add ESC and Default cases to normalStateHandler |
16 | ||
17 | const csiStateHandler: {[key: string]: (handler: IInputHandler, parser: Parser) => void} = {}; | |
18 | csiStateHandler['?'] = (_, parser) => parser.setPrefix('?'); | |
19 | csiStateHandler['>'] = (_, parser) => parser.setPrefix('>'); | |
20 | csiStateHandler['!'] = (_, parser) => parser.setPrefix('!'); | |
21 | csiStateHandler['0'] = (_, parser) => parser.setParam(parser.getParam() * 10); | |
22 | csiStateHandler['1'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 1); | |
23 | csiStateHandler['2'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 2); | |
24 | csiStateHandler['3'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 3); | |
25 | csiStateHandler['4'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 4); | |
26 | csiStateHandler['5'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 5); | |
27 | csiStateHandler['6'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 6); | |
28 | csiStateHandler['7'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 7); | |
29 | csiStateHandler['8'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 8); | |
30 | csiStateHandler['9'] = (_, parser) => parser.setParam(parser.getParam() * 10 + 9); | |
635a4d76 DI |
31 | csiStateHandler['$'] = (_, parser) => parser.setPostfix('$'); |
32 | csiStateHandler['"'] = (_, parser) => parser.setPostfix('"'); | |
33 | csiStateHandler[' '] = (_, parser) => parser.setPostfix(' '); | |
34 | csiStateHandler['\''] = (_, parser) => parser.setPostfix('\''); | |
35 | // TODO: Add remaining CSI cases | |
a31921ae DI |
36 | |
37 | enum ParserState { | |
38 | NORMAL = 0, | |
39 | ESCAPED = 1, | |
40 | CSI = 2, | |
41 | OSC = 3, | |
42 | CHARSET = 4, | |
43 | DCS = 5, | |
44 | IGNORE = 6 | |
45 | } | |
46 | ||
47 | export class Parser { | |
48 | private state: ParserState; | |
49 | ||
50 | // TODO: Remove terminal when handler can do everything | |
51 | constructor( | |
52 | private _inputHandler: IInputHandler, | |
53 | private _terminal: any | |
54 | ) { | |
55 | this.state = ParserState.NORMAL; | |
56 | } | |
57 | ||
58 | public parse(data: string) { | |
59 | let l = data.length, i = 0, j, cs, ch, code, low, ch_width, row; | |
60 | ||
61 | // apply leftover surrogate high from last write | |
62 | if (this._terminal.surrogate_high) { | |
63 | data = this._terminal.surrogate_high + data; | |
64 | this._terminal.surrogate_high = ''; | |
65 | } | |
66 | ||
67 | for (; i < l; i++) { | |
68 | ch = data[i]; | |
69 | ||
70 | // FIXME: higher chars than 0xa0 are not allowed in escape sequences | |
71 | // --> maybe move to default | |
72 | code = data.charCodeAt(i); | |
73 | if (0xD800 <= code && code <= 0xDBFF) { | |
74 | // we got a surrogate high | |
75 | // get surrogate low (next 2 bytes) | |
991b2843 | 76 | low = data.charCodeAt(i + 1); |
a31921ae DI |
77 | if (isNaN(low)) { |
78 | // end of data stream, save surrogate high | |
79 | this._terminal.surrogate_high = ch; | |
80 | continue; | |
81 | } | |
82 | code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; | |
991b2843 | 83 | ch += data.charAt(i + 1); |
a31921ae DI |
84 | } |
85 | // surrogate low - already handled above | |
86 | if (0xDC00 <= code && code <= 0xDFFF) | |
87 | continue; | |
88 | ||
89 | if (this.state === ParserState.NORMAL) { | |
90 | if (ch in normalStateHandler) { | |
91 | normalStateHandler[ch](this._inputHandler); | |
92 | // Skip switch statement (eventually everything will be handled this way | |
93 | continue; | |
94 | } | |
95 | } | |
96 | ||
97 | switch (this.state) { | |
98 | case ParserState.NORMAL: | |
99 | switch (ch) { | |
100 | case C0.ESC: | |
101 | this.state = ParserState.ESCAPED; | |
102 | break; | |
103 | ||
104 | default: | |
105 | // ' ' | |
106 | // calculate print space | |
107 | // expensive call, therefore we save width in line buffer | |
108 | ch_width = wcwidth(code); | |
109 | ||
110 | if (ch >= ' ') { | |
991b2843 DI |
111 | if (this._terminal.charset && this._terminal.charset[ch]) { |
112 | ch = this._terminal.charset[ch]; | |
a31921ae DI |
113 | } |
114 | ||
991b2843 | 115 | row = this._terminal.y + this._terminal.ybase; |
a31921ae DI |
116 | |
117 | // insert combining char in last cell | |
118 | // FIXME: needs handling after cursor jumps | |
991b2843 | 119 | if (!ch_width && this._terminal.x) { |
a31921ae | 120 | // dont overflow left |
991b2843 DI |
121 | if (this._terminal.lines.get(row)[this._terminal.x - 1]) { |
122 | if (!this._terminal.lines.get(row)[this._terminal.x - 1][2]) { | |
a31921ae DI |
123 | |
124 | // found empty cell after fullwidth, need to go 2 cells back | |
991b2843 DI |
125 | if (this._terminal.lines.get(row)[this._terminal.x - 2]) |
126 | this._terminal.lines.get(row)[this._terminal.x - 2][1] += ch; | |
a31921ae DI |
127 | |
128 | } else { | |
991b2843 | 129 | this._terminal.lines.get(row)[this._terminal.x - 1][1] += ch; |
a31921ae | 130 | } |
991b2843 | 131 | this._terminal.updateRange(this._terminal.y); |
a31921ae DI |
132 | } |
133 | break; | |
134 | } | |
135 | ||
136 | // goto next line if ch would overflow | |
137 | // TODO: needs a global min terminal width of 2 | |
991b2843 | 138 | if (this._terminal.x + ch_width - 1 >= this._terminal.cols) { |
a31921ae | 139 | // autowrap - DECAWM |
991b2843 DI |
140 | if (this._terminal.wraparoundMode) { |
141 | this._terminal.x = 0; | |
142 | this._terminal.y++; | |
143 | if (this._terminal.y > this._terminal.scrollBottom) { | |
144 | this._terminal.y--; | |
145 | this._terminal.scroll(); | |
a31921ae DI |
146 | } |
147 | } else { | |
991b2843 DI |
148 | this._terminal.x = this._terminal.cols - 1; |
149 | if (ch_width === 2) // FIXME: check for xterm behavior | |
a31921ae DI |
150 | continue; |
151 | } | |
152 | } | |
991b2843 | 153 | row = this._terminal.y + this._terminal.ybase; |
a31921ae DI |
154 | |
155 | // insert mode: move characters to right | |
991b2843 | 156 | if (this._terminal.insertMode) { |
a31921ae | 157 | // do this twice for a fullwidth char |
991b2843 | 158 | for (let moves = 0; moves < ch_width; ++moves) { |
a31921ae DI |
159 | // remove last cell, if it's width is 0 |
160 | // we have to adjust the second last cell as well | |
991b2843 DI |
161 | const removed = this._terminal.lines.get(this._terminal.y + this._terminal.ybase).pop(); |
162 | if (removed[2] === 0 | |
163 | && this._terminal.lines.get(row)[this._terminal.cols - 2] | |
164 | && this._terminal.lines.get(row)[this._terminal.cols - 2][2] === 2) | |
165 | this._terminal.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1]; | |
a31921ae DI |
166 | |
167 | // insert empty cell at cursor | |
991b2843 | 168 | this._terminal.lines.get(row).splice(this._terminal.x, 0, [this._terminal.curAttr, ' ', 1]); |
a31921ae DI |
169 | } |
170 | } | |
171 | ||
991b2843 DI |
172 | this._terminal.lines.get(row)[this._terminal.x] = [this._terminal.curAttr, ch, ch_width]; |
173 | this._terminal.x++; | |
174 | this._terminal.updateRange(this._terminal.y); | |
a31921ae DI |
175 | |
176 | // fullwidth char - set next cell width to zero and advance cursor | |
991b2843 DI |
177 | if (ch_width === 2) { |
178 | this._terminal.lines.get(row)[this._terminal.x] = [this._terminal.curAttr, '', 0]; | |
179 | this._terminal.x++; | |
a31921ae DI |
180 | } |
181 | } | |
182 | break; | |
183 | } | |
184 | break; | |
185 | case ParserState.ESCAPED: | |
186 | switch (ch) { | |
187 | // ESC [ Control Sequence Introducer ( CSI is 0x9b). | |
188 | case '[': | |
991b2843 DI |
189 | this._terminal.params = []; |
190 | this._terminal.currentParam = 0; | |
191 | this.state = ParserState.CSI; | |
a31921ae DI |
192 | break; |
193 | ||
194 | // ESC ] Operating System Command ( OSC is 0x9d). | |
195 | case ']': | |
991b2843 DI |
196 | this._terminal.params = []; |
197 | this._terminal.currentParam = 0; | |
198 | this.state = ParserState.OSC; | |
a31921ae DI |
199 | break; |
200 | ||
201 | // ESC P Device Control String ( DCS is 0x90). | |
202 | case 'P': | |
991b2843 DI |
203 | this._terminal.params = []; |
204 | this._terminal.currentParam = 0; | |
205 | this.state = ParserState.DCS; | |
a31921ae DI |
206 | break; |
207 | ||
208 | // ESC _ Application Program Command ( APC is 0x9f). | |
209 | case '_': | |
210 | this.state = ParserState.IGNORE; | |
211 | break; | |
212 | ||
213 | // ESC ^ Privacy Message ( PM is 0x9e). | |
214 | case '^': | |
215 | this.state = ParserState.IGNORE; | |
216 | break; | |
217 | ||
218 | // ESC c Full Reset (RIS). | |
219 | case 'c': | |
991b2843 | 220 | this._terminal.reset(); |
a31921ae DI |
221 | break; |
222 | ||
223 | // ESC E Next Line ( NEL is 0x85). | |
224 | // ESC D Index ( IND is 0x84). | |
225 | case 'E': | |
991b2843 | 226 | this._terminal.x = 0; |
a31921ae DI |
227 | ; |
228 | case 'D': | |
991b2843 | 229 | this._terminal.index(); |
6deaaa8e | 230 | this.state = ParserState.NORMAL; |
a31921ae DI |
231 | break; |
232 | ||
233 | // ESC M Reverse Index ( RI is 0x8d). | |
234 | case 'M': | |
991b2843 | 235 | this._terminal.reverseIndex(); |
6deaaa8e | 236 | this.state = ParserState.NORMAL; |
a31921ae DI |
237 | break; |
238 | ||
239 | // ESC % Select default/utf-8 character set. | |
240 | // @ = default, G = utf-8 | |
241 | case '%': | |
991b2843 DI |
242 | // this.charset = null; |
243 | this._terminal.setgLevel(0); | |
1848fff4 | 244 | this._terminal.setgCharset(0, CHARSETS.US); |
a31921ae DI |
245 | this.state = ParserState.NORMAL; |
246 | i++; | |
247 | break; | |
248 | ||
249 | // ESC (,),*,+,-,. Designate G0-G2 Character Set. | |
250 | case '(': // <-- this seems to get all the attention | |
251 | case ')': | |
252 | case '*': | |
253 | case '+': | |
254 | case '-': | |
255 | case '.': | |
256 | switch (ch) { | |
257 | case '(': | |
991b2843 | 258 | this._terminal.gcharset = 0; |
a31921ae DI |
259 | break; |
260 | case ')': | |
991b2843 | 261 | this._terminal.gcharset = 1; |
a31921ae DI |
262 | break; |
263 | case '*': | |
991b2843 | 264 | this._terminal.gcharset = 2; |
a31921ae DI |
265 | break; |
266 | case '+': | |
991b2843 | 267 | this._terminal.gcharset = 3; |
a31921ae DI |
268 | break; |
269 | case '-': | |
991b2843 | 270 | this._terminal.gcharset = 1; |
a31921ae DI |
271 | break; |
272 | case '.': | |
991b2843 | 273 | this._terminal.gcharset = 2; |
a31921ae DI |
274 | break; |
275 | } | |
991b2843 | 276 | this.state = ParserState.CHARSET; |
a31921ae DI |
277 | break; |
278 | ||
279 | // Designate G3 Character Set (VT300). | |
280 | // A = ISO Latin-1 Supplemental. | |
281 | // Not implemented. | |
282 | case '/': | |
991b2843 | 283 | this._terminal.gcharset = 3; |
a31921ae DI |
284 | this.state = ParserState.CHARSET; |
285 | i--; | |
286 | break; | |
287 | ||
288 | // ESC N | |
289 | // Single Shift Select of G2 Character Set | |
290 | // ( SS2 is 0x8e). This affects next character only. | |
291 | case 'N': | |
292 | break; | |
293 | // ESC O | |
294 | // Single Shift Select of G3 Character Set | |
295 | // ( SS3 is 0x8f). This affects next character only. | |
296 | case 'O': | |
297 | break; | |
298 | // ESC n | |
299 | // Invoke the G2 Character Set as GL (LS2). | |
300 | case 'n': | |
991b2843 | 301 | this._terminal.setgLevel(2); |
a31921ae DI |
302 | break; |
303 | // ESC o | |
304 | // Invoke the G3 Character Set as GL (LS3). | |
305 | case 'o': | |
991b2843 | 306 | this._terminal.setgLevel(3); |
a31921ae DI |
307 | break; |
308 | // ESC | | |
309 | // Invoke the G3 Character Set as GR (LS3R). | |
310 | case '|': | |
991b2843 | 311 | this._terminal.setgLevel(3); |
a31921ae DI |
312 | break; |
313 | // ESC } | |
314 | // Invoke the G2 Character Set as GR (LS2R). | |
315 | case '}': | |
991b2843 | 316 | this._terminal.setgLevel(2); |
a31921ae DI |
317 | break; |
318 | // ESC ~ | |
319 | // Invoke the G1 Character Set as GR (LS1R). | |
320 | case '~': | |
991b2843 | 321 | this._terminal.setgLevel(1); |
a31921ae DI |
322 | break; |
323 | ||
324 | // ESC 7 Save Cursor (DECSC). | |
325 | case '7': | |
991b2843 | 326 | this._terminal.saveCursor(); |
a31921ae DI |
327 | this.state = ParserState.NORMAL; |
328 | break; | |
329 | ||
330 | // ESC 8 Restore Cursor (DECRC). | |
331 | case '8': | |
991b2843 | 332 | this._terminal.restoreCursor(); |
a31921ae DI |
333 | this.state = ParserState.NORMAL; |
334 | break; | |
335 | ||
336 | // ESC # 3 DEC line height/width | |
337 | case '#': | |
338 | this.state = ParserState.NORMAL; | |
339 | i++; | |
340 | break; | |
341 | ||
342 | // ESC H Tab Set (HTS is 0x88). | |
343 | case 'H': | |
991b2843 | 344 | this._terminal.tabSet(); |
6deaaa8e | 345 | this.state = ParserState.NORMAL; |
a31921ae DI |
346 | break; |
347 | ||
348 | // ESC = Application Keypad (DECKPAM). | |
349 | case '=': | |
991b2843 DI |
350 | this._terminal.log('Serial port requested application keypad.'); |
351 | this._terminal.applicationKeypad = true; | |
352 | this._terminal.viewport.syncScrollArea(); | |
a31921ae DI |
353 | this.state = ParserState.NORMAL; |
354 | break; | |
355 | ||
356 | // ESC > Normal Keypad (DECKPNM). | |
357 | case '>': | |
991b2843 DI |
358 | this._terminal.log('Switching back to normal keypad.'); |
359 | this._terminal.applicationKeypad = false; | |
360 | this._terminal.viewport.syncScrollArea(); | |
a31921ae DI |
361 | this.state = ParserState.NORMAL; |
362 | break; | |
363 | ||
364 | default: | |
365 | this.state = ParserState.NORMAL; | |
991b2843 | 366 | this._terminal.error('Unknown ESC control: %s.', ch); |
a31921ae DI |
367 | break; |
368 | } | |
369 | break; | |
370 | ||
371 | case ParserState.CHARSET: | |
372 | switch (ch) { | |
373 | case '0': // DEC Special Character and Line Drawing Set. | |
1848fff4 | 374 | cs = CHARSETS.SCLD; |
a31921ae DI |
375 | break; |
376 | case 'A': // UK | |
1848fff4 | 377 | cs = CHARSETS.UK; |
a31921ae DI |
378 | break; |
379 | case 'B': // United States (USASCII). | |
1848fff4 | 380 | cs = CHARSETS.US; |
a31921ae DI |
381 | break; |
382 | case '4': // Dutch | |
1848fff4 | 383 | cs = CHARSETS.Dutch; |
a31921ae DI |
384 | break; |
385 | case 'C': // Finnish | |
386 | case '5': | |
1848fff4 | 387 | cs = CHARSETS.Finnish; |
a31921ae DI |
388 | break; |
389 | case 'R': // French | |
1848fff4 | 390 | cs = CHARSETS.French; |
a31921ae DI |
391 | break; |
392 | case 'Q': // FrenchCanadian | |
1848fff4 | 393 | cs = CHARSETS.FrenchCanadian; |
a31921ae DI |
394 | break; |
395 | case 'K': // German | |
1848fff4 | 396 | cs = CHARSETS.German; |
a31921ae DI |
397 | break; |
398 | case 'Y': // Italian | |
1848fff4 | 399 | cs = CHARSETS.Italian; |
a31921ae DI |
400 | break; |
401 | case 'E': // NorwegianDanish | |
402 | case '6': | |
1848fff4 | 403 | cs = CHARSETS.NorwegianDanish; |
a31921ae DI |
404 | break; |
405 | case 'Z': // Spanish | |
1848fff4 | 406 | cs = CHARSETS.Spanish; |
a31921ae DI |
407 | break; |
408 | case 'H': // Swedish | |
409 | case '7': | |
1848fff4 | 410 | cs = CHARSETS.Swedish; |
a31921ae DI |
411 | break; |
412 | case '=': // Swiss | |
1848fff4 | 413 | cs = CHARSETS.Swiss; |
a31921ae DI |
414 | break; |
415 | case '/': // ISOLatin (actually /A) | |
1848fff4 | 416 | cs = CHARSETS.ISOLatin; |
a31921ae DI |
417 | i++; |
418 | break; | |
419 | default: // Default | |
1848fff4 | 420 | cs = CHARSETS.US; |
a31921ae DI |
421 | break; |
422 | } | |
991b2843 DI |
423 | this._terminal.setgCharset(this._terminal.gcharset, cs); |
424 | this._terminal.gcharset = null; | |
a31921ae DI |
425 | this.state = ParserState.NORMAL; |
426 | break; | |
427 | ||
428 | case ParserState.OSC: | |
429 | // OSC Ps ; Pt ST | |
430 | // OSC Ps ; Pt BEL | |
431 | // Set Text Parameters. | |
432 | if (ch === C0.ESC || ch === C0.BEL) { | |
433 | if (ch === C0.ESC) i++; | |
434 | ||
991b2843 | 435 | this._terminal.params.push(this._terminal.currentParam); |
a31921ae | 436 | |
991b2843 | 437 | switch (this._terminal.params[0]) { |
a31921ae DI |
438 | case 0: |
439 | case 1: | |
440 | case 2: | |
991b2843 DI |
441 | if (this._terminal.params[1]) { |
442 | this._terminal.title = this._terminal.params[1]; | |
443 | this._terminal.handleTitle(this._terminal.title); | |
a31921ae DI |
444 | } |
445 | break; | |
446 | case 3: | |
447 | // set X property | |
448 | break; | |
449 | case 4: | |
450 | case 5: | |
451 | // change dynamic colors | |
452 | break; | |
453 | case 10: | |
454 | case 11: | |
455 | case 12: | |
456 | case 13: | |
457 | case 14: | |
458 | case 15: | |
459 | case 16: | |
460 | case 17: | |
461 | case 18: | |
462 | case 19: | |
463 | // change dynamic ui colors | |
464 | break; | |
465 | case 46: | |
466 | // change log file | |
467 | break; | |
468 | case 50: | |
469 | // dynamic font | |
470 | break; | |
471 | case 51: | |
472 | // emacs shell | |
473 | break; | |
474 | case 52: | |
475 | // manipulate selection data | |
476 | break; | |
477 | case 104: | |
478 | case 105: | |
479 | case 110: | |
480 | case 111: | |
481 | case 112: | |
482 | case 113: | |
483 | case 114: | |
484 | case 115: | |
485 | case 116: | |
486 | case 117: | |
487 | case 118: | |
488 | // reset colors | |
489 | break; | |
490 | } | |
491 | ||
991b2843 DI |
492 | this._terminal.params = []; |
493 | this._terminal.currentParam = 0; | |
a31921ae DI |
494 | this.state = ParserState.NORMAL; |
495 | } else { | |
991b2843 | 496 | if (!this._terminal.params.length) { |
a31921ae | 497 | if (ch >= '0' && ch <= '9') { |
991b2843 DI |
498 | this._terminal.currentParam = |
499 | this._terminal.currentParam * 10 + ch.charCodeAt(0) - 48; | |
a31921ae | 500 | } else if (ch === ';') { |
991b2843 DI |
501 | this._terminal.params.push(this._terminal.currentParam); |
502 | this._terminal.currentParam = ''; | |
a31921ae DI |
503 | } |
504 | } else { | |
991b2843 | 505 | this._terminal.currentParam += ch; |
a31921ae DI |
506 | } |
507 | } | |
508 | break; | |
509 | ||
991b2843 | 510 | case ParserState.CSI: |
672dc1a2 DI |
511 | if (ch in csiStateHandler) { |
512 | csiStateHandler[ch](this._inputHandler, this); | |
513 | // Skip below switch as this has handled these codes (eventually everything will be handled here | |
a31921ae DI |
514 | break; |
515 | } | |
516 | ||
991b2843 DI |
517 | this._terminal.params.push(this._terminal.currentParam); |
518 | this._terminal.currentParam = 0; | |
a31921ae DI |
519 | |
520 | // ';' | |
521 | if (ch === ';') break; | |
522 | ||
523 | this.state = ParserState.NORMAL; | |
524 | ||
525 | switch (ch) { | |
526 | // CSI Ps A | |
527 | // Cursor Up Ps Times (default = 1) (CUU). | |
528 | case 'A': | |
991b2843 | 529 | this._terminal.cursorUp(this._terminal.params); |
a31921ae DI |
530 | break; |
531 | ||
532 | // CSI Ps B | |
533 | // Cursor Down Ps Times (default = 1) (CUD). | |
534 | case 'B': | |
991b2843 | 535 | this._terminal.cursorDown(this._terminal.params); |
a31921ae DI |
536 | break; |
537 | ||
538 | // CSI Ps C | |
539 | // Cursor Forward Ps Times (default = 1) (CUF). | |
540 | case 'C': | |
991b2843 | 541 | this._terminal.cursorForward(this._terminal.params); |
a31921ae DI |
542 | break; |
543 | ||
544 | // CSI Ps D | |
545 | // Cursor Backward Ps Times (default = 1) (CUB). | |
546 | case 'D': | |
991b2843 | 547 | this._terminal.cursorBackward(this._terminal.params); |
a31921ae DI |
548 | break; |
549 | ||
550 | // CSI Ps ; Ps H | |
551 | // Cursor Position [row;column] (default = [1,1]) (CUP). | |
552 | case 'H': | |
991b2843 | 553 | this._terminal.cursorPos(this._terminal.params); |
a31921ae DI |
554 | break; |
555 | ||
556 | // CSI Ps J Erase in Display (ED). | |
557 | case 'J': | |
991b2843 | 558 | this._terminal.eraseInDisplay(this._terminal.params); |
a31921ae DI |
559 | break; |
560 | ||
561 | // CSI Ps K Erase in Line (EL). | |
562 | case 'K': | |
991b2843 | 563 | this._terminal.eraseInLine(this._terminal.params); |
a31921ae DI |
564 | break; |
565 | ||
566 | // CSI Pm m Character Attributes (SGR). | |
567 | case 'm': | |
991b2843 DI |
568 | if (!this._terminal.prefix) { |
569 | this._terminal.charAttributes(this._terminal.params); | |
a31921ae DI |
570 | } |
571 | break; | |
572 | ||
573 | // CSI Ps n Device Status Report (DSR). | |
574 | case 'n': | |
991b2843 DI |
575 | if (!this._terminal.prefix) { |
576 | this._terminal.deviceStatus(this._terminal.params); | |
a31921ae DI |
577 | } |
578 | break; | |
579 | ||
580 | /** | |
581 | * Additions | |
582 | */ | |
583 | ||
584 | // CSI Ps @ | |
585 | // Insert Ps (Blank) Character(s) (default = 1) (ICH). | |
586 | case '@': | |
991b2843 | 587 | this._terminal.insertChars(this._terminal.params); |
a31921ae DI |
588 | break; |
589 | ||
590 | // CSI Ps E | |
591 | // Cursor Next Line Ps Times (default = 1) (CNL). | |
592 | case 'E': | |
991b2843 | 593 | this._terminal.cursorNextLine(this._terminal.params); |
a31921ae DI |
594 | break; |
595 | ||
596 | // CSI Ps F | |
597 | // Cursor Preceding Line Ps Times (default = 1) (CNL). | |
598 | case 'F': | |
991b2843 | 599 | this._terminal.cursorPrecedingLine(this._terminal.params); |
a31921ae DI |
600 | break; |
601 | ||
602 | // CSI Ps G | |
603 | // Cursor Character Absolute [column] (default = [row,1]) (CHA). | |
604 | case 'G': | |
991b2843 | 605 | this._terminal.cursorCharAbsolute(this._terminal.params); |
a31921ae DI |
606 | break; |
607 | ||
608 | // CSI Ps L | |
609 | // Insert Ps Line(s) (default = 1) (IL). | |
610 | case 'L': | |
991b2843 | 611 | this._terminal.insertLines(this._terminal.params); |
a31921ae DI |
612 | break; |
613 | ||
614 | // CSI Ps M | |
615 | // Delete Ps Line(s) (default = 1) (DL). | |
616 | case 'M': | |
991b2843 | 617 | this._terminal.deleteLines(this._terminal.params); |
a31921ae DI |
618 | break; |
619 | ||
620 | // CSI Ps P | |
621 | // Delete Ps Character(s) (default = 1) (DCH). | |
622 | case 'P': | |
991b2843 | 623 | this._terminal.deleteChars(this._terminal.params); |
a31921ae DI |
624 | break; |
625 | ||
626 | // CSI Ps X | |
627 | // Erase Ps Character(s) (default = 1) (ECH). | |
628 | case 'X': | |
991b2843 | 629 | this._terminal.eraseChars(this._terminal.params); |
a31921ae DI |
630 | break; |
631 | ||
632 | // CSI Pm ` Character Position Absolute | |
633 | // [column] (default = [row,1]) (HPA). | |
634 | case '`': | |
991b2843 | 635 | this._terminal.charPosAbsolute(this._terminal.params); |
a31921ae DI |
636 | break; |
637 | ||
638 | // 141 61 a * HPR - | |
639 | // Horizontal Position Relative | |
640 | case 'a': | |
991b2843 | 641 | this._terminal.HPositionRelative(this._terminal.params); |
a31921ae DI |
642 | break; |
643 | ||
644 | // CSI P s c | |
645 | // Send Device Attributes (Primary DA). | |
646 | // CSI > P s c | |
647 | // Send Device Attributes (Secondary DA) | |
648 | case 'c': | |
991b2843 | 649 | this._terminal.sendDeviceAttributes(this._terminal.params); |
a31921ae DI |
650 | break; |
651 | ||
652 | // CSI Pm d | |
653 | // Line Position Absolute [row] (default = [1,column]) (VPA). | |
654 | case 'd': | |
991b2843 | 655 | this._terminal.linePosAbsolute(this._terminal.params); |
a31921ae DI |
656 | break; |
657 | ||
658 | // 145 65 e * VPR - Vertical Position Relative | |
659 | case 'e': | |
991b2843 | 660 | this._terminal.VPositionRelative(this._terminal.params); |
a31921ae DI |
661 | break; |
662 | ||
663 | // CSI Ps ; Ps f | |
664 | // Horizontal and Vertical Position [row;column] (default = | |
665 | // [1,1]) (HVP). | |
666 | case 'f': | |
991b2843 | 667 | this._terminal.HVPosition(this._terminal.params); |
a31921ae DI |
668 | break; |
669 | ||
670 | // CSI Pm h Set Mode (SM). | |
671 | // CSI ? Pm h - mouse escape codes, cursor escape codes | |
672 | case 'h': | |
991b2843 | 673 | this._terminal.setMode(this._terminal.params); |
a31921ae DI |
674 | break; |
675 | ||
676 | // CSI Pm l Reset Mode (RM). | |
677 | // CSI ? Pm l | |
678 | case 'l': | |
991b2843 | 679 | this._terminal.resetMode(this._terminal.params); |
a31921ae DI |
680 | break; |
681 | ||
682 | // CSI Ps ; Ps r | |
683 | // Set Scrolling Region [top;bottom] (default = full size of win- | |
684 | // dow) (DECSTBM). | |
685 | // CSI ? Pm r | |
686 | case 'r': | |
991b2843 | 687 | this._terminal.setScrollRegion(this._terminal.params); |
a31921ae DI |
688 | break; |
689 | ||
690 | // CSI s | |
691 | // Save cursor (ANSI.SYS). | |
692 | case 's': | |
991b2843 | 693 | this._terminal.saveCursor(this._terminal.params); |
a31921ae DI |
694 | break; |
695 | ||
696 | // CSI u | |
697 | // Restore cursor (ANSI.SYS). | |
698 | case 'u': | |
991b2843 | 699 | this._terminal.restoreCursor(this._terminal.params); |
a31921ae DI |
700 | break; |
701 | ||
702 | /** | |
703 | * Lesser Used | |
704 | */ | |
705 | ||
706 | // CSI Ps I | |
707 | // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). | |
708 | case 'I': | |
991b2843 | 709 | this._terminal.cursorForwardTab(this._terminal.params); |
a31921ae DI |
710 | break; |
711 | ||
712 | // CSI Ps S Scroll up Ps lines (default = 1) (SU). | |
713 | case 'S': | |
991b2843 | 714 | this._terminal.scrollUp(this._terminal.params); |
a31921ae DI |
715 | break; |
716 | ||
717 | // CSI Ps T Scroll down Ps lines (default = 1) (SD). | |
718 | // CSI Ps ; Ps ; Ps ; Ps ; Ps T | |
719 | // CSI > Ps; Ps T | |
720 | case 'T': | |
721 | // if (this.prefix === '>') { | |
722 | // this.resetTitleModes(this.params); | |
723 | // break; | |
724 | // } | |
725 | // if (this.params.length > 2) { | |
726 | // this.initMouseTracking(this.params); | |
727 | // break; | |
728 | // } | |
991b2843 DI |
729 | if (this._terminal.params.length < 2 && !this._terminal.prefix) { |
730 | this._terminal.scrollDown(this._terminal.params); | |
a31921ae DI |
731 | } |
732 | break; | |
733 | ||
734 | // CSI Ps Z | |
735 | // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). | |
736 | case 'Z': | |
991b2843 | 737 | this._terminal.cursorBackwardTab(this._terminal.params); |
a31921ae DI |
738 | break; |
739 | ||
740 | // CSI Ps b Repeat the preceding graphic character Ps times (REP). | |
741 | case 'b': | |
991b2843 | 742 | this._terminal.repeatPrecedingCharacter(this._terminal.params); |
a31921ae DI |
743 | break; |
744 | ||
745 | // CSI Ps g Tab Clear (TBC). | |
746 | case 'g': | |
991b2843 | 747 | this._terminal.tabClear(this._terminal.params); |
a31921ae DI |
748 | break; |
749 | ||
750 | // CSI Pm i Media Copy (MC). | |
751 | // CSI ? Pm i | |
752 | // case 'i': | |
753 | // this.mediaCopy(this.params); | |
754 | // break; | |
755 | ||
756 | // CSI Pm m Character Attributes (SGR). | |
757 | // CSI > Ps; Ps m | |
758 | // case 'm': // duplicate | |
759 | // if (this.prefix === '>') { | |
760 | // this.setResources(this.params); | |
761 | // } else { | |
762 | // this.charAttributes(this.params); | |
763 | // } | |
764 | // break; | |
765 | ||
766 | // CSI Ps n Device Status Report (DSR). | |
767 | // CSI > Ps n | |
768 | // case 'n': // duplicate | |
769 | // if (this.prefix === '>') { | |
770 | // this.disableModifiers(this.params); | |
771 | // } else { | |
772 | // this.deviceStatus(this.params); | |
773 | // } | |
774 | // break; | |
775 | ||
776 | // CSI > Ps p Set pointer mode. | |
777 | // CSI ! p Soft terminal reset (DECSTR). | |
778 | // CSI Ps$ p | |
779 | // Request ANSI mode (DECRQM). | |
780 | // CSI ? Ps$ p | |
781 | // Request DEC private mode (DECRQM). | |
782 | // CSI Ps ; Ps " p | |
783 | case 'p': | |
991b2843 | 784 | switch (this._terminal.prefix) { |
a31921ae DI |
785 | // case '>': |
786 | // this.setPointerMode(this.params); | |
787 | // break; | |
788 | case '!': | |
991b2843 | 789 | this._terminal.softReset(this._terminal.params); |
a31921ae DI |
790 | break; |
791 | // case '?': | |
792 | // if (this.postfix === '$') { | |
793 | // this.requestPrivateMode(this.params); | |
794 | // } | |
795 | // break; | |
796 | // default: | |
797 | // if (this.postfix === '"') { | |
798 | // this.setConformanceLevel(this.params); | |
799 | // } else if (this.postfix === '$') { | |
800 | // this.requestAnsiMode(this.params); | |
801 | // } | |
802 | // break; | |
803 | } | |
804 | break; | |
805 | ||
806 | // CSI Ps q Load LEDs (DECLL). | |
807 | // CSI Ps SP q | |
808 | // CSI Ps " q | |
809 | // case 'q': | |
810 | // if (this.postfix === ' ') { | |
811 | // this.setCursorStyle(this.params); | |
812 | // break; | |
813 | // } | |
814 | // if (this.postfix === '"') { | |
815 | // this.setCharProtectionAttr(this.params); | |
816 | // break; | |
817 | // } | |
818 | // this.loadLEDs(this.params); | |
819 | // break; | |
820 | ||
821 | // CSI Ps ; Ps r | |
822 | // Set Scrolling Region [top;bottom] (default = full size of win- | |
823 | // dow) (DECSTBM). | |
824 | // CSI ? Pm r | |
825 | // CSI Pt; Pl; Pb; Pr; Ps$ r | |
826 | // case 'r': // duplicate | |
827 | // if (this.prefix === '?') { | |
828 | // this.restorePrivateValues(this.params); | |
829 | // } else if (this.postfix === '$') { | |
830 | // this.setAttrInRectangle(this.params); | |
831 | // } else { | |
832 | // this.setScrollRegion(this.params); | |
833 | // } | |
834 | // break; | |
835 | ||
836 | // CSI s Save cursor (ANSI.SYS). | |
837 | // CSI ? Pm s | |
838 | // case 's': // duplicate | |
839 | // if (this.prefix === '?') { | |
840 | // this.savePrivateValues(this.params); | |
841 | // } else { | |
842 | // this.saveCursor(this.params); | |
843 | // } | |
844 | // break; | |
845 | ||
846 | // CSI Ps ; Ps ; Ps t | |
847 | // CSI Pt; Pl; Pb; Pr; Ps$ t | |
848 | // CSI > Ps; Ps t | |
849 | // CSI Ps SP t | |
850 | // case 't': | |
851 | // if (this.postfix === '$') { | |
852 | // this.reverseAttrInRectangle(this.params); | |
853 | // } else if (this.postfix === ' ') { | |
854 | // this.setWarningBellVolume(this.params); | |
855 | // } else { | |
856 | // if (this.prefix === '>') { | |
857 | // this.setTitleModeFeature(this.params); | |
858 | // } else { | |
859 | // this.manipulateWindow(this.params); | |
860 | // } | |
861 | // } | |
862 | // break; | |
863 | ||
864 | // CSI u Restore cursor (ANSI.SYS). | |
865 | // CSI Ps SP u | |
866 | // case 'u': // duplicate | |
867 | // if (this.postfix === ' ') { | |
868 | // this.setMarginBellVolume(this.params); | |
869 | // } else { | |
870 | // this.restoreCursor(this.params); | |
871 | // } | |
872 | // break; | |
873 | ||
874 | // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v | |
875 | // case 'v': | |
876 | // if (this.postfix === '$') { | |
877 | // this.copyRectagle(this.params); | |
878 | // } | |
879 | // break; | |
880 | ||
881 | // CSI Pt ; Pl ; Pb ; Pr ' w | |
882 | // case 'w': | |
883 | // if (this.postfix === '\'') { | |
884 | // this.enableFilterRectangle(this.params); | |
885 | // } | |
886 | // break; | |
887 | ||
888 | // CSI Ps x Request Terminal Parameters (DECREQTPARM). | |
889 | // CSI Ps x Select Attribute Change Extent (DECSACE). | |
890 | // CSI Pc; Pt; Pl; Pb; Pr$ x | |
891 | // case 'x': | |
892 | // if (this.postfix === '$') { | |
893 | // this.fillRectangle(this.params); | |
894 | // } else { | |
895 | // this.requestParameters(this.params); | |
896 | // //this.__(this.params); | |
897 | // } | |
898 | // break; | |
899 | ||
900 | // CSI Ps ; Pu ' z | |
901 | // CSI Pt; Pl; Pb; Pr$ z | |
902 | // case 'z': | |
903 | // if (this.postfix === '\'') { | |
904 | // this.enableLocatorReporting(this.params); | |
905 | // } else if (this.postfix === '$') { | |
906 | // this.eraseRectangle(this.params); | |
907 | // } | |
908 | // break; | |
909 | ||
910 | // CSI Pm ' { | |
911 | // CSI Pt; Pl; Pb; Pr$ { | |
912 | // case '{': | |
913 | // if (this.postfix === '\'') { | |
914 | // this.setLocatorEvents(this.params); | |
915 | // } else if (this.postfix === '$') { | |
916 | // this.selectiveEraseRectangle(this.params); | |
917 | // } | |
918 | // break; | |
919 | ||
920 | // CSI Ps ' | | |
921 | // case '|': | |
922 | // if (this.postfix === '\'') { | |
923 | // this.requestLocatorPosition(this.params); | |
924 | // } | |
925 | // break; | |
926 | ||
927 | // CSI P m SP } | |
928 | // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. | |
929 | // case '}': | |
930 | // if (this.postfix === ' ') { | |
931 | // this.insertColumns(this.params); | |
932 | // } | |
933 | // break; | |
934 | ||
935 | // CSI P m SP ~ | |
936 | // Delete P s Column(s) (default = 1) (DECDC), VT420 and up | |
937 | // case '~': | |
938 | // if (this.postfix === ' ') { | |
939 | // this.deleteColumns(this.params); | |
940 | // } | |
941 | // break; | |
942 | ||
943 | default: | |
991b2843 | 944 | this._terminal.error('Unknown CSI code: %s.', ch); |
a31921ae DI |
945 | break; |
946 | } | |
947 | ||
991b2843 DI |
948 | this._terminal.prefix = ''; |
949 | this._terminal.postfix = ''; | |
a31921ae DI |
950 | break; |
951 | ||
952 | case ParserState.DCS: | |
953 | if (ch === C0.ESC || ch === C0.BEL) { | |
954 | if (ch === C0.ESC) i++; | |
955 | ||
991b2843 | 956 | switch (this._terminal.prefix) { |
a31921ae DI |
957 | // User-Defined Keys (DECUDK). |
958 | case '': | |
959 | break; | |
960 | ||
961 | // Request Status String (DECRQSS). | |
962 | // test: echo -e '\eP$q"p\e\\' | |
963 | case '$q': | |
991b2843 | 964 | let pt = this._terminal.currentParam |
a31921ae DI |
965 | , valid = false; |
966 | ||
967 | switch (pt) { | |
968 | // DECSCA | |
969 | case '"q': | |
970 | pt = '0"q'; | |
971 | break; | |
972 | ||
973 | // DECSCL | |
974 | case '"p': | |
975 | pt = '61"p'; | |
976 | break; | |
977 | ||
978 | // DECSTBM | |
979 | case 'r': | |
980 | pt = '' | |
991b2843 | 981 | + (this._terminal.scrollTop + 1) |
a31921ae | 982 | + ';' |
991b2843 | 983 | + (this._terminal.scrollBottom + 1) |
a31921ae DI |
984 | + 'r'; |
985 | break; | |
986 | ||
987 | // SGR | |
988 | case 'm': | |
989 | pt = '0m'; | |
990 | break; | |
991 | ||
992 | default: | |
991b2843 | 993 | this._terminal.error('Unknown DCS Pt: %s.', pt); |
a31921ae DI |
994 | pt = ''; |
995 | break; | |
996 | } | |
997 | ||
991b2843 | 998 | this._terminal.send(C0.ESC + 'P' + +valid + '$r' + pt + C0.ESC + '\\'); |
a31921ae DI |
999 | break; |
1000 | ||
1001 | // Set Termcap/Terminfo Data (xterm, experimental). | |
1002 | case '+p': | |
1003 | break; | |
1004 | ||
1005 | // Request Termcap/Terminfo String (xterm, experimental) | |
1006 | // Regular xterm does not even respond to this sequence. | |
1007 | // This can cause a small glitch in vim. | |
1008 | // test: echo -ne '\eP+q6b64\e\\' | |
1009 | case '+q': | |
991b2843 DI |
1010 | // TODO: Don't declare pt twice |
1011 | /*let*/ pt = this._terminal.currentParam | |
a31921ae DI |
1012 | , valid = false; |
1013 | ||
991b2843 | 1014 | this._terminal.send(C0.ESC + 'P' + +valid + '+r' + pt + C0.ESC + '\\'); |
a31921ae DI |
1015 | break; |
1016 | ||
1017 | default: | |
991b2843 | 1018 | this._terminal.error('Unknown DCS prefix: %s.', this._terminal.prefix); |
a31921ae DI |
1019 | break; |
1020 | } | |
1021 | ||
991b2843 DI |
1022 | this._terminal.currentParam = 0; |
1023 | this._terminal.prefix = ''; | |
a31921ae | 1024 | this.state = ParserState.NORMAL; |
991b2843 DI |
1025 | } else if (!this._terminal.currentParam) { |
1026 | if (!this._terminal.prefix && ch !== '$' && ch !== '+') { | |
1027 | this._terminal.currentParam = ch; | |
1028 | } else if (this._terminal.prefix.length === 2) { | |
1029 | this._terminal.currentParam = ch; | |
a31921ae | 1030 | } else { |
991b2843 | 1031 | this._terminal.prefix += ch; |
a31921ae DI |
1032 | } |
1033 | } else { | |
991b2843 | 1034 | this._terminal.currentParam += ch; |
a31921ae DI |
1035 | } |
1036 | break; | |
1037 | ||
1038 | case ParserState.IGNORE: | |
1039 | // For PM and APC. | |
1040 | if (ch === C0.ESC || ch === C0.BEL) { | |
1041 | if (ch === C0.ESC) i++; | |
1042 | this.state = ParserState.NORMAL; | |
1043 | } | |
1044 | break; | |
1045 | } | |
1046 | } | |
1047 | } | |
672dc1a2 | 1048 | |
635a4d76 | 1049 | public setPrefix(prefix: string): void { |
672dc1a2 DI |
1050 | this._terminal.prefix = prefix; |
1051 | } | |
1052 | ||
1053 | public setParam(param: number) { | |
1054 | this._terminal.currentParam = param; | |
1055 | } | |
1056 | ||
1057 | public getParam(): number { | |
1058 | return this._terminal.currentParam; | |
1059 | } | |
635a4d76 DI |
1060 | |
1061 | public setPostfix(postfix: string): void { | |
1062 | this._terminal.postfix = postfix; | |
1063 | } | |
a31921ae | 1064 | } |
4a27b9a5 | 1065 | |
940bd269 | 1066 | const wcwidth = (function(opts) { |
4a27b9a5 DI |
1067 | // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c |
1068 | // combining characters | |
940bd269 | 1069 | const COMBINING = [ |
4a27b9a5 DI |
1070 | [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], |
1071 | [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], | |
1072 | [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], | |
1073 | [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], | |
1074 | [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], | |
1075 | [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], | |
1076 | [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], | |
1077 | [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], | |
1078 | [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], | |
1079 | [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], | |
1080 | [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], | |
1081 | [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], | |
1082 | [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], | |
1083 | [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], | |
1084 | [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], | |
1085 | [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], | |
1086 | [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], | |
1087 | [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], | |
1088 | [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], | |
1089 | [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], | |
1090 | [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], | |
1091 | [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], | |
1092 | [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], | |
1093 | [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], | |
1094 | [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], | |
1095 | [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], | |
1096 | [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], | |
1097 | [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], | |
1098 | [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], | |
1099 | [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], | |
1100 | [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], | |
1101 | [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], | |
1102 | [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], | |
1103 | [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], | |
1104 | [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], | |
1105 | [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], | |
1106 | [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], | |
1107 | [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], | |
1108 | [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], | |
1109 | [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], | |
1110 | [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], | |
1111 | [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], | |
1112 | [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB], | |
1113 | [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], | |
1114 | [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], | |
1115 | [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], | |
1116 | [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], | |
1117 | [0xE0100, 0xE01EF] | |
1118 | ]; | |
1119 | // binary search | |
1120 | function bisearch(ucs) { | |
940bd269 DI |
1121 | let min = 0; |
1122 | let max = COMBINING.length - 1; | |
1123 | let mid; | |
4a27b9a5 DI |
1124 | if (ucs < COMBINING[0][0] || ucs > COMBINING[max][1]) |
1125 | return false; | |
1126 | while (max >= min) { | |
1127 | mid = Math.floor((min + max) / 2); | |
1128 | if (ucs > COMBINING[mid][1]) | |
1129 | min = mid + 1; | |
1130 | else if (ucs < COMBINING[mid][0]) | |
1131 | max = mid - 1; | |
1132 | else | |
1133 | return true; | |
1134 | } | |
1135 | return false; | |
1136 | } | |
1137 | function wcwidth(ucs) { | |
1138 | // test for 8-bit control characters | |
1139 | if (ucs === 0) | |
1140 | return opts.nul; | |
1141 | if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) | |
1142 | return opts.control; | |
1143 | // binary search in table of non-spacing characters | |
1144 | if (bisearch(ucs)) | |
1145 | return 0; | |
1146 | // if we arrive here, ucs is not a combining or C0/C1 control character | |
940bd269 DI |
1147 | if (isWide(ucs)) { |
1148 | return 2; | |
1149 | } | |
1150 | return 1; | |
1151 | } | |
1152 | function isWide(ucs) { | |
1153 | return ( | |
1154 | ucs >= 0x1100 && ( | |
4a27b9a5 | 1155 | ucs <= 0x115f || // Hangul Jamo init. consonants |
940bd269 DI |
1156 | ucs === 0x2329 || |
1157 | ucs === 0x232a || | |
1158 | (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi | |
4a27b9a5 DI |
1159 | (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables |
1160 | (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs | |
1161 | (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms | |
1162 | (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms | |
1163 | (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms | |
1164 | (ucs >= 0xffe0 && ucs <= 0xffe6) || | |
1165 | (ucs >= 0x20000 && ucs <= 0x2fffd) || | |
940bd269 | 1166 | (ucs >= 0x30000 && ucs <= 0x3fffd))); |
4a27b9a5 DI |
1167 | } |
1168 | return wcwidth; | |
1169 | })({nul: 0, control: 0}); // configurable options |