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