11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst { 41cb0ef41Sopenharmony_ci ArrayPrototypeSlice, 51cb0ef41Sopenharmony_ci ArrayPrototypeSort, 61cb0ef41Sopenharmony_ci RegExpPrototypeExec, 71cb0ef41Sopenharmony_ci StringFromCharCode, 81cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt, 91cb0ef41Sopenharmony_ci StringPrototypeCodePointAt, 101cb0ef41Sopenharmony_ci StringPrototypeSlice, 111cb0ef41Sopenharmony_ci StringPrototypeToLowerCase, 121cb0ef41Sopenharmony_ci Symbol, 131cb0ef41Sopenharmony_ci} = primordials; 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ciconst kUTF16SurrogateThreshold = 0x10000; // 2 ** 16 161cb0ef41Sopenharmony_ciconst kEscape = '\x1b'; 171cb0ef41Sopenharmony_ciconst kSubstringSearch = Symbol('kSubstringSearch'); 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_cifunction CSI(strings, ...args) { 201cb0ef41Sopenharmony_ci let ret = `${kEscape}[`; 211cb0ef41Sopenharmony_ci for (let n = 0; n < strings.length; n++) { 221cb0ef41Sopenharmony_ci ret += strings[n]; 231cb0ef41Sopenharmony_ci if (n < args.length) 241cb0ef41Sopenharmony_ci ret += args[n]; 251cb0ef41Sopenharmony_ci } 261cb0ef41Sopenharmony_ci return ret; 271cb0ef41Sopenharmony_ci} 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ciCSI.kEscape = kEscape; 301cb0ef41Sopenharmony_ciCSI.kClearToLineBeginning = CSI`1K`; 311cb0ef41Sopenharmony_ciCSI.kClearToLineEnd = CSI`0K`; 321cb0ef41Sopenharmony_ciCSI.kClearLine = CSI`2K`; 331cb0ef41Sopenharmony_ciCSI.kClearScreenDown = CSI`0J`; 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci// TODO(BridgeAR): Treat combined characters as single character, i.e, 361cb0ef41Sopenharmony_ci// 'a\u0301' and '\u0301a' (both have the same visual output). 371cb0ef41Sopenharmony_ci// Check Canonical_Combining_Class in 381cb0ef41Sopenharmony_ci// http://userguide.icu-project.org/strings/properties 391cb0ef41Sopenharmony_cifunction charLengthLeft(str, i) { 401cb0ef41Sopenharmony_ci if (i <= 0) 411cb0ef41Sopenharmony_ci return 0; 421cb0ef41Sopenharmony_ci if ((i > 1 && 431cb0ef41Sopenharmony_ci StringPrototypeCodePointAt(str, i - 2) >= kUTF16SurrogateThreshold) || 441cb0ef41Sopenharmony_ci StringPrototypeCodePointAt(str, i - 1) >= kUTF16SurrogateThreshold) { 451cb0ef41Sopenharmony_ci return 2; 461cb0ef41Sopenharmony_ci } 471cb0ef41Sopenharmony_ci return 1; 481cb0ef41Sopenharmony_ci} 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_cifunction charLengthAt(str, i) { 511cb0ef41Sopenharmony_ci if (str.length <= i) { 521cb0ef41Sopenharmony_ci // Pretend to move to the right. This is necessary to autocomplete while 531cb0ef41Sopenharmony_ci // moving to the right. 541cb0ef41Sopenharmony_ci return 1; 551cb0ef41Sopenharmony_ci } 561cb0ef41Sopenharmony_ci return StringPrototypeCodePointAt(str, i) >= kUTF16SurrogateThreshold ? 2 : 1; 571cb0ef41Sopenharmony_ci} 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci/* 601cb0ef41Sopenharmony_ci Some patterns seen in terminal key escape codes, derived from combos seen 611cb0ef41Sopenharmony_ci at http://www.midnight-commander.org/browser/lib/tty/key.c 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci ESC letter 641cb0ef41Sopenharmony_ci ESC [ letter 651cb0ef41Sopenharmony_ci ESC [ modifier letter 661cb0ef41Sopenharmony_ci ESC [ 1 ; modifier letter 671cb0ef41Sopenharmony_ci ESC [ num char 681cb0ef41Sopenharmony_ci ESC [ num ; modifier char 691cb0ef41Sopenharmony_ci ESC O letter 701cb0ef41Sopenharmony_ci ESC O modifier letter 711cb0ef41Sopenharmony_ci ESC O 1 ; modifier letter 721cb0ef41Sopenharmony_ci ESC N letter 731cb0ef41Sopenharmony_ci ESC [ [ num ; modifier char 741cb0ef41Sopenharmony_ci ESC [ [ 1 ; modifier letter 751cb0ef41Sopenharmony_ci ESC ESC [ num char 761cb0ef41Sopenharmony_ci ESC ESC O letter 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci - char is usually ~ but $ and ^ also happen with rxvt 791cb0ef41Sopenharmony_ci - modifier is 1 + 801cb0ef41Sopenharmony_ci (shift * 1) + 811cb0ef41Sopenharmony_ci (left_alt * 2) + 821cb0ef41Sopenharmony_ci (ctrl * 4) + 831cb0ef41Sopenharmony_ci (right_alt * 8) 841cb0ef41Sopenharmony_ci - two leading ESCs apparently mean the same as one leading ESC 851cb0ef41Sopenharmony_ci*/ 861cb0ef41Sopenharmony_cifunction* emitKeys(stream) { 871cb0ef41Sopenharmony_ci while (true) { 881cb0ef41Sopenharmony_ci let ch = yield; 891cb0ef41Sopenharmony_ci let s = ch; 901cb0ef41Sopenharmony_ci let escaped = false; 911cb0ef41Sopenharmony_ci const key = { 921cb0ef41Sopenharmony_ci sequence: null, 931cb0ef41Sopenharmony_ci name: undefined, 941cb0ef41Sopenharmony_ci ctrl: false, 951cb0ef41Sopenharmony_ci meta: false, 961cb0ef41Sopenharmony_ci shift: false, 971cb0ef41Sopenharmony_ci }; 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci if (ch === kEscape) { 1001cb0ef41Sopenharmony_ci escaped = true; 1011cb0ef41Sopenharmony_ci s += (ch = yield); 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_ci if (ch === kEscape) { 1041cb0ef41Sopenharmony_ci s += (ch = yield); 1051cb0ef41Sopenharmony_ci } 1061cb0ef41Sopenharmony_ci } 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_ci if (escaped && (ch === 'O' || ch === '[')) { 1091cb0ef41Sopenharmony_ci // ANSI escape sequence 1101cb0ef41Sopenharmony_ci let code = ch; 1111cb0ef41Sopenharmony_ci let modifier = 0; 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci if (ch === 'O') { 1141cb0ef41Sopenharmony_ci // ESC O letter 1151cb0ef41Sopenharmony_ci // ESC O modifier letter 1161cb0ef41Sopenharmony_ci s += (ch = yield); 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ci if (ch >= '0' && ch <= '9') { 1191cb0ef41Sopenharmony_ci modifier = (ch >> 0) - 1; 1201cb0ef41Sopenharmony_ci s += (ch = yield); 1211cb0ef41Sopenharmony_ci } 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci code += ch; 1241cb0ef41Sopenharmony_ci } else if (ch === '[') { 1251cb0ef41Sopenharmony_ci // ESC [ letter 1261cb0ef41Sopenharmony_ci // ESC [ modifier letter 1271cb0ef41Sopenharmony_ci // ESC [ [ modifier letter 1281cb0ef41Sopenharmony_ci // ESC [ [ num char 1291cb0ef41Sopenharmony_ci s += (ch = yield); 1301cb0ef41Sopenharmony_ci 1311cb0ef41Sopenharmony_ci if (ch === '[') { 1321cb0ef41Sopenharmony_ci // \x1b[[A 1331cb0ef41Sopenharmony_ci // ^--- escape codes might have a second bracket 1341cb0ef41Sopenharmony_ci code += ch; 1351cb0ef41Sopenharmony_ci s += (ch = yield); 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci /* 1391cb0ef41Sopenharmony_ci * Here and later we try to buffer just enough data to get 1401cb0ef41Sopenharmony_ci * a complete ascii sequence. 1411cb0ef41Sopenharmony_ci * 1421cb0ef41Sopenharmony_ci * We have basically two classes of ascii characters to process: 1431cb0ef41Sopenharmony_ci * 1441cb0ef41Sopenharmony_ci * 1451cb0ef41Sopenharmony_ci * 1. `\x1b[24;5~` should be parsed as { code: '[24~', modifier: 5 } 1461cb0ef41Sopenharmony_ci * 1471cb0ef41Sopenharmony_ci * This particular example is featuring Ctrl+F12 in xterm. 1481cb0ef41Sopenharmony_ci * 1491cb0ef41Sopenharmony_ci * - `;5` part is optional, e.g. it could be `\x1b[24~` 1501cb0ef41Sopenharmony_ci * - first part can contain one or two digits 1511cb0ef41Sopenharmony_ci * - there is also special case when there can be 3 digits 1521cb0ef41Sopenharmony_ci * but without modifier. They are the case of paste bracket mode 1531cb0ef41Sopenharmony_ci * 1541cb0ef41Sopenharmony_ci * So the generic regexp is like /^(?:\d\d?(;\d)?[~^$]|\d{3}~)$/ 1551cb0ef41Sopenharmony_ci * 1561cb0ef41Sopenharmony_ci * 1571cb0ef41Sopenharmony_ci * 2. `\x1b[1;5H` should be parsed as { code: '[H', modifier: 5 } 1581cb0ef41Sopenharmony_ci * 1591cb0ef41Sopenharmony_ci * This particular example is featuring Ctrl+Home in xterm. 1601cb0ef41Sopenharmony_ci * 1611cb0ef41Sopenharmony_ci * - `1;5` part is optional, e.g. it could be `\x1b[H` 1621cb0ef41Sopenharmony_ci * - `1;` part is optional, e.g. it could be `\x1b[5H` 1631cb0ef41Sopenharmony_ci * 1641cb0ef41Sopenharmony_ci * So the generic regexp is like /^((\d;)?\d)?[A-Za-z]$/ 1651cb0ef41Sopenharmony_ci * 1661cb0ef41Sopenharmony_ci */ 1671cb0ef41Sopenharmony_ci const cmdStart = s.length - 1; 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci // Skip one or two leading digits 1701cb0ef41Sopenharmony_ci if (ch >= '0' && ch <= '9') { 1711cb0ef41Sopenharmony_ci s += (ch = yield); 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_ci if (ch >= '0' && ch <= '9') { 1741cb0ef41Sopenharmony_ci s += (ch = yield); 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci if (ch >= '0' && ch <= '9') { 1771cb0ef41Sopenharmony_ci s += (ch = yield); 1781cb0ef41Sopenharmony_ci } 1791cb0ef41Sopenharmony_ci } 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci // skip modifier 1831cb0ef41Sopenharmony_ci if (ch === ';') { 1841cb0ef41Sopenharmony_ci s += (ch = yield); 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_ci if (ch >= '0' && ch <= '9') { 1871cb0ef41Sopenharmony_ci s += yield; 1881cb0ef41Sopenharmony_ci } 1891cb0ef41Sopenharmony_ci } 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci /* 1921cb0ef41Sopenharmony_ci * We buffered enough data, now trying to extract code 1931cb0ef41Sopenharmony_ci * and modifier from it 1941cb0ef41Sopenharmony_ci */ 1951cb0ef41Sopenharmony_ci const cmd = StringPrototypeSlice(s, cmdStart); 1961cb0ef41Sopenharmony_ci let match; 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci if ((match = RegExpPrototypeExec(/^(?:(\d\d?)(?:;(\d))?([~^$])|(\d{3}~))$/, cmd))) { 1991cb0ef41Sopenharmony_ci if (match[4]) { 2001cb0ef41Sopenharmony_ci code += match[4]; 2011cb0ef41Sopenharmony_ci } else { 2021cb0ef41Sopenharmony_ci code += match[1] + match[3]; 2031cb0ef41Sopenharmony_ci modifier = (match[2] || 1) - 1; 2041cb0ef41Sopenharmony_ci } 2051cb0ef41Sopenharmony_ci } else if ( 2061cb0ef41Sopenharmony_ci (match = RegExpPrototypeExec(/^((\d;)?(\d))?([A-Za-z])$/, cmd)) 2071cb0ef41Sopenharmony_ci ) { 2081cb0ef41Sopenharmony_ci code += match[4]; 2091cb0ef41Sopenharmony_ci modifier = (match[3] || 1) - 1; 2101cb0ef41Sopenharmony_ci } else { 2111cb0ef41Sopenharmony_ci code += cmd; 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci } 2141cb0ef41Sopenharmony_ci 2151cb0ef41Sopenharmony_ci // Parse the key modifier 2161cb0ef41Sopenharmony_ci key.ctrl = !!(modifier & 4); 2171cb0ef41Sopenharmony_ci key.meta = !!(modifier & 10); 2181cb0ef41Sopenharmony_ci key.shift = !!(modifier & 1); 2191cb0ef41Sopenharmony_ci key.code = code; 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci // Parse the key itself 2221cb0ef41Sopenharmony_ci switch (code) { 2231cb0ef41Sopenharmony_ci /* xterm/gnome ESC [ letter (with modifier) */ 2241cb0ef41Sopenharmony_ci case '[P': key.name = 'f1'; break; 2251cb0ef41Sopenharmony_ci case '[Q': key.name = 'f2'; break; 2261cb0ef41Sopenharmony_ci case '[R': key.name = 'f3'; break; 2271cb0ef41Sopenharmony_ci case '[S': key.name = 'f4'; break; 2281cb0ef41Sopenharmony_ci 2291cb0ef41Sopenharmony_ci /* xterm/gnome ESC O letter (without modifier) */ 2301cb0ef41Sopenharmony_ci case 'OP': key.name = 'f1'; break; 2311cb0ef41Sopenharmony_ci case 'OQ': key.name = 'f2'; break; 2321cb0ef41Sopenharmony_ci case 'OR': key.name = 'f3'; break; 2331cb0ef41Sopenharmony_ci case 'OS': key.name = 'f4'; break; 2341cb0ef41Sopenharmony_ci 2351cb0ef41Sopenharmony_ci /* xterm/rxvt ESC [ number ~ */ 2361cb0ef41Sopenharmony_ci case '[11~': key.name = 'f1'; break; 2371cb0ef41Sopenharmony_ci case '[12~': key.name = 'f2'; break; 2381cb0ef41Sopenharmony_ci case '[13~': key.name = 'f3'; break; 2391cb0ef41Sopenharmony_ci case '[14~': key.name = 'f4'; break; 2401cb0ef41Sopenharmony_ci 2411cb0ef41Sopenharmony_ci /* paste bracket mode */ 2421cb0ef41Sopenharmony_ci case '[200~': key.name = 'paste-start'; break; 2431cb0ef41Sopenharmony_ci case '[201~': key.name = 'paste-end'; break; 2441cb0ef41Sopenharmony_ci 2451cb0ef41Sopenharmony_ci /* from Cygwin and used in libuv */ 2461cb0ef41Sopenharmony_ci case '[[A': key.name = 'f1'; break; 2471cb0ef41Sopenharmony_ci case '[[B': key.name = 'f2'; break; 2481cb0ef41Sopenharmony_ci case '[[C': key.name = 'f3'; break; 2491cb0ef41Sopenharmony_ci case '[[D': key.name = 'f4'; break; 2501cb0ef41Sopenharmony_ci case '[[E': key.name = 'f5'; break; 2511cb0ef41Sopenharmony_ci 2521cb0ef41Sopenharmony_ci /* common */ 2531cb0ef41Sopenharmony_ci case '[15~': key.name = 'f5'; break; 2541cb0ef41Sopenharmony_ci case '[17~': key.name = 'f6'; break; 2551cb0ef41Sopenharmony_ci case '[18~': key.name = 'f7'; break; 2561cb0ef41Sopenharmony_ci case '[19~': key.name = 'f8'; break; 2571cb0ef41Sopenharmony_ci case '[20~': key.name = 'f9'; break; 2581cb0ef41Sopenharmony_ci case '[21~': key.name = 'f10'; break; 2591cb0ef41Sopenharmony_ci case '[23~': key.name = 'f11'; break; 2601cb0ef41Sopenharmony_ci case '[24~': key.name = 'f12'; break; 2611cb0ef41Sopenharmony_ci 2621cb0ef41Sopenharmony_ci /* xterm ESC [ letter */ 2631cb0ef41Sopenharmony_ci case '[A': key.name = 'up'; break; 2641cb0ef41Sopenharmony_ci case '[B': key.name = 'down'; break; 2651cb0ef41Sopenharmony_ci case '[C': key.name = 'right'; break; 2661cb0ef41Sopenharmony_ci case '[D': key.name = 'left'; break; 2671cb0ef41Sopenharmony_ci case '[E': key.name = 'clear'; break; 2681cb0ef41Sopenharmony_ci case '[F': key.name = 'end'; break; 2691cb0ef41Sopenharmony_ci case '[H': key.name = 'home'; break; 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci /* xterm/gnome ESC O letter */ 2721cb0ef41Sopenharmony_ci case 'OA': key.name = 'up'; break; 2731cb0ef41Sopenharmony_ci case 'OB': key.name = 'down'; break; 2741cb0ef41Sopenharmony_ci case 'OC': key.name = 'right'; break; 2751cb0ef41Sopenharmony_ci case 'OD': key.name = 'left'; break; 2761cb0ef41Sopenharmony_ci case 'OE': key.name = 'clear'; break; 2771cb0ef41Sopenharmony_ci case 'OF': key.name = 'end'; break; 2781cb0ef41Sopenharmony_ci case 'OH': key.name = 'home'; break; 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci /* xterm/rxvt ESC [ number ~ */ 2811cb0ef41Sopenharmony_ci case '[1~': key.name = 'home'; break; 2821cb0ef41Sopenharmony_ci case '[2~': key.name = 'insert'; break; 2831cb0ef41Sopenharmony_ci case '[3~': key.name = 'delete'; break; 2841cb0ef41Sopenharmony_ci case '[4~': key.name = 'end'; break; 2851cb0ef41Sopenharmony_ci case '[5~': key.name = 'pageup'; break; 2861cb0ef41Sopenharmony_ci case '[6~': key.name = 'pagedown'; break; 2871cb0ef41Sopenharmony_ci 2881cb0ef41Sopenharmony_ci /* putty */ 2891cb0ef41Sopenharmony_ci case '[[5~': key.name = 'pageup'; break; 2901cb0ef41Sopenharmony_ci case '[[6~': key.name = 'pagedown'; break; 2911cb0ef41Sopenharmony_ci 2921cb0ef41Sopenharmony_ci /* rxvt */ 2931cb0ef41Sopenharmony_ci case '[7~': key.name = 'home'; break; 2941cb0ef41Sopenharmony_ci case '[8~': key.name = 'end'; break; 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci /* rxvt keys with modifiers */ 2971cb0ef41Sopenharmony_ci case '[a': key.name = 'up'; key.shift = true; break; 2981cb0ef41Sopenharmony_ci case '[b': key.name = 'down'; key.shift = true; break; 2991cb0ef41Sopenharmony_ci case '[c': key.name = 'right'; key.shift = true; break; 3001cb0ef41Sopenharmony_ci case '[d': key.name = 'left'; key.shift = true; break; 3011cb0ef41Sopenharmony_ci case '[e': key.name = 'clear'; key.shift = true; break; 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci case '[2$': key.name = 'insert'; key.shift = true; break; 3041cb0ef41Sopenharmony_ci case '[3$': key.name = 'delete'; key.shift = true; break; 3051cb0ef41Sopenharmony_ci case '[5$': key.name = 'pageup'; key.shift = true; break; 3061cb0ef41Sopenharmony_ci case '[6$': key.name = 'pagedown'; key.shift = true; break; 3071cb0ef41Sopenharmony_ci case '[7$': key.name = 'home'; key.shift = true; break; 3081cb0ef41Sopenharmony_ci case '[8$': key.name = 'end'; key.shift = true; break; 3091cb0ef41Sopenharmony_ci 3101cb0ef41Sopenharmony_ci case 'Oa': key.name = 'up'; key.ctrl = true; break; 3111cb0ef41Sopenharmony_ci case 'Ob': key.name = 'down'; key.ctrl = true; break; 3121cb0ef41Sopenharmony_ci case 'Oc': key.name = 'right'; key.ctrl = true; break; 3131cb0ef41Sopenharmony_ci case 'Od': key.name = 'left'; key.ctrl = true; break; 3141cb0ef41Sopenharmony_ci case 'Oe': key.name = 'clear'; key.ctrl = true; break; 3151cb0ef41Sopenharmony_ci 3161cb0ef41Sopenharmony_ci case '[2^': key.name = 'insert'; key.ctrl = true; break; 3171cb0ef41Sopenharmony_ci case '[3^': key.name = 'delete'; key.ctrl = true; break; 3181cb0ef41Sopenharmony_ci case '[5^': key.name = 'pageup'; key.ctrl = true; break; 3191cb0ef41Sopenharmony_ci case '[6^': key.name = 'pagedown'; key.ctrl = true; break; 3201cb0ef41Sopenharmony_ci case '[7^': key.name = 'home'; key.ctrl = true; break; 3211cb0ef41Sopenharmony_ci case '[8^': key.name = 'end'; key.ctrl = true; break; 3221cb0ef41Sopenharmony_ci 3231cb0ef41Sopenharmony_ci /* misc. */ 3241cb0ef41Sopenharmony_ci case '[Z': key.name = 'tab'; key.shift = true; break; 3251cb0ef41Sopenharmony_ci default: key.name = 'undefined'; break; 3261cb0ef41Sopenharmony_ci } 3271cb0ef41Sopenharmony_ci } else if (ch === '\r') { 3281cb0ef41Sopenharmony_ci // carriage return 3291cb0ef41Sopenharmony_ci key.name = 'return'; 3301cb0ef41Sopenharmony_ci key.meta = escaped; 3311cb0ef41Sopenharmony_ci } else if (ch === '\n') { 3321cb0ef41Sopenharmony_ci // Enter, should have been called linefeed 3331cb0ef41Sopenharmony_ci key.name = 'enter'; 3341cb0ef41Sopenharmony_ci key.meta = escaped; 3351cb0ef41Sopenharmony_ci } else if (ch === '\t') { 3361cb0ef41Sopenharmony_ci // tab 3371cb0ef41Sopenharmony_ci key.name = 'tab'; 3381cb0ef41Sopenharmony_ci key.meta = escaped; 3391cb0ef41Sopenharmony_ci } else if (ch === '\b' || ch === '\x7f') { 3401cb0ef41Sopenharmony_ci // backspace or ctrl+h 3411cb0ef41Sopenharmony_ci key.name = 'backspace'; 3421cb0ef41Sopenharmony_ci key.meta = escaped; 3431cb0ef41Sopenharmony_ci } else if (ch === kEscape) { 3441cb0ef41Sopenharmony_ci // escape key 3451cb0ef41Sopenharmony_ci key.name = 'escape'; 3461cb0ef41Sopenharmony_ci key.meta = escaped; 3471cb0ef41Sopenharmony_ci } else if (ch === ' ') { 3481cb0ef41Sopenharmony_ci key.name = 'space'; 3491cb0ef41Sopenharmony_ci key.meta = escaped; 3501cb0ef41Sopenharmony_ci } else if (!escaped && ch <= '\x1a') { 3511cb0ef41Sopenharmony_ci // ctrl+letter 3521cb0ef41Sopenharmony_ci key.name = StringFromCharCode( 3531cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(ch) + StringPrototypeCharCodeAt('a') - 1, 3541cb0ef41Sopenharmony_ci ); 3551cb0ef41Sopenharmony_ci key.ctrl = true; 3561cb0ef41Sopenharmony_ci } else if (RegExpPrototypeExec(/^[0-9A-Za-z]$/, ch) !== null) { 3571cb0ef41Sopenharmony_ci // Letter, number, shift+letter 3581cb0ef41Sopenharmony_ci key.name = StringPrototypeToLowerCase(ch); 3591cb0ef41Sopenharmony_ci key.shift = RegExpPrototypeExec(/^[A-Z]$/, ch) !== null; 3601cb0ef41Sopenharmony_ci key.meta = escaped; 3611cb0ef41Sopenharmony_ci } else if (escaped) { 3621cb0ef41Sopenharmony_ci // Escape sequence timeout 3631cb0ef41Sopenharmony_ci key.name = ch.length ? undefined : 'escape'; 3641cb0ef41Sopenharmony_ci key.meta = true; 3651cb0ef41Sopenharmony_ci } 3661cb0ef41Sopenharmony_ci 3671cb0ef41Sopenharmony_ci key.sequence = s; 3681cb0ef41Sopenharmony_ci 3691cb0ef41Sopenharmony_ci if (s.length !== 0 && (key.name !== undefined || escaped)) { 3701cb0ef41Sopenharmony_ci /* Named character or sequence */ 3711cb0ef41Sopenharmony_ci stream.emit('keypress', escaped ? undefined : s, key); 3721cb0ef41Sopenharmony_ci } else if (charLengthAt(s, 0) === s.length) { 3731cb0ef41Sopenharmony_ci /* Single unnamed character, e.g. "." */ 3741cb0ef41Sopenharmony_ci stream.emit('keypress', s, key); 3751cb0ef41Sopenharmony_ci } 3761cb0ef41Sopenharmony_ci /* Unrecognized or broken escape sequence, don't emit anything */ 3771cb0ef41Sopenharmony_ci } 3781cb0ef41Sopenharmony_ci} 3791cb0ef41Sopenharmony_ci 3801cb0ef41Sopenharmony_ci// This runs in O(n log n). 3811cb0ef41Sopenharmony_cifunction commonPrefix(strings) { 3821cb0ef41Sopenharmony_ci if (strings.length === 0) { 3831cb0ef41Sopenharmony_ci return ''; 3841cb0ef41Sopenharmony_ci } 3851cb0ef41Sopenharmony_ci if (strings.length === 1) { 3861cb0ef41Sopenharmony_ci return strings[0]; 3871cb0ef41Sopenharmony_ci } 3881cb0ef41Sopenharmony_ci const sorted = ArrayPrototypeSort(ArrayPrototypeSlice(strings)); 3891cb0ef41Sopenharmony_ci const min = sorted[0]; 3901cb0ef41Sopenharmony_ci const max = sorted[sorted.length - 1]; 3911cb0ef41Sopenharmony_ci for (let i = 0; i < min.length; i++) { 3921cb0ef41Sopenharmony_ci if (min[i] !== max[i]) { 3931cb0ef41Sopenharmony_ci return StringPrototypeSlice(min, 0, i); 3941cb0ef41Sopenharmony_ci } 3951cb0ef41Sopenharmony_ci } 3961cb0ef41Sopenharmony_ci return min; 3971cb0ef41Sopenharmony_ci} 3981cb0ef41Sopenharmony_ci 3991cb0ef41Sopenharmony_cimodule.exports = { 4001cb0ef41Sopenharmony_ci charLengthAt, 4011cb0ef41Sopenharmony_ci charLengthLeft, 4021cb0ef41Sopenharmony_ci commonPrefix, 4031cb0ef41Sopenharmony_ci emitKeys, 4041cb0ef41Sopenharmony_ci kSubstringSearch, 4051cb0ef41Sopenharmony_ci CSI, 4061cb0ef41Sopenharmony_ci}; 407