11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst {
41cb0ef41Sopenharmony_ci  ArrayPrototypeFilter,
51cb0ef41Sopenharmony_ci  ArrayPrototypeIncludes,
61cb0ef41Sopenharmony_ci  ArrayPrototypeMap,
71cb0ef41Sopenharmony_ci  Boolean,
81cb0ef41Sopenharmony_ci  FunctionPrototypeBind,
91cb0ef41Sopenharmony_ci  MathMin,
101cb0ef41Sopenharmony_ci  RegExpPrototypeExec,
111cb0ef41Sopenharmony_ci  SafeSet,
121cb0ef41Sopenharmony_ci  SafeStringIterator,
131cb0ef41Sopenharmony_ci  StringPrototypeEndsWith,
141cb0ef41Sopenharmony_ci  StringPrototypeIndexOf,
151cb0ef41Sopenharmony_ci  StringPrototypeLastIndexOf,
161cb0ef41Sopenharmony_ci  StringPrototypeReplaceAll,
171cb0ef41Sopenharmony_ci  StringPrototypeSlice,
181cb0ef41Sopenharmony_ci  StringPrototypeStartsWith,
191cb0ef41Sopenharmony_ci  StringPrototypeToLowerCase,
201cb0ef41Sopenharmony_ci  StringPrototypeTrim,
211cb0ef41Sopenharmony_ci  Symbol,
221cb0ef41Sopenharmony_ci} = primordials;
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ciconst { tokTypes: tt, Parser: AcornParser } =
251cb0ef41Sopenharmony_ci  require('internal/deps/acorn/acorn/dist/acorn');
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ciconst { sendInspectorCommand } = require('internal/util/inspector');
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciconst {
301cb0ef41Sopenharmony_ci  ERR_INSPECTOR_NOT_AVAILABLE,
311cb0ef41Sopenharmony_ci} = require('internal/errors').codes;
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ciconst {
341cb0ef41Sopenharmony_ci  clearLine,
351cb0ef41Sopenharmony_ci  clearScreenDown,
361cb0ef41Sopenharmony_ci  cursorTo,
371cb0ef41Sopenharmony_ci  moveCursor,
381cb0ef41Sopenharmony_ci} = require('internal/readline/callbacks');
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ciconst {
411cb0ef41Sopenharmony_ci  commonPrefix,
421cb0ef41Sopenharmony_ci  kSubstringSearch,
431cb0ef41Sopenharmony_ci} = require('internal/readline/utils');
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ciconst {
461cb0ef41Sopenharmony_ci  getStringWidth,
471cb0ef41Sopenharmony_ci  inspect,
481cb0ef41Sopenharmony_ci} = require('internal/util/inspect');
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_cilet debug = require('internal/util/debuglog').debuglog('repl', (fn) => {
511cb0ef41Sopenharmony_ci  debug = fn;
521cb0ef41Sopenharmony_ci});
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ciconst previewOptions = {
551cb0ef41Sopenharmony_ci  colors: false,
561cb0ef41Sopenharmony_ci  depth: 1,
571cb0ef41Sopenharmony_ci  showHidden: false,
581cb0ef41Sopenharmony_ci};
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ciconst REPL_MODE_STRICT = Symbol('repl-strict');
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci// If the error is that we've unexpectedly ended the input,
631cb0ef41Sopenharmony_ci// then let the user try to recover by adding more input.
641cb0ef41Sopenharmony_ci// Note: `e` (the original exception) is not used by the current implementation,
651cb0ef41Sopenharmony_ci// but may be needed in the future.
661cb0ef41Sopenharmony_cifunction isRecoverableError(e, code) {
671cb0ef41Sopenharmony_ci  // For similar reasons as `defaultEval`, wrap expressions starting with a
681cb0ef41Sopenharmony_ci  // curly brace with parenthesis.  Note: only the open parenthesis is added
691cb0ef41Sopenharmony_ci  // here as the point is to test for potentially valid but incomplete
701cb0ef41Sopenharmony_ci  // expressions.
711cb0ef41Sopenharmony_ci  if (RegExpPrototypeExec(/^\s*\{/, code) !== null &&
721cb0ef41Sopenharmony_ci      isRecoverableError(e, `(${code}`))
731cb0ef41Sopenharmony_ci    return true;
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci  let recoverable = false;
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci  // Determine if the point of any error raised is at the end of the input.
781cb0ef41Sopenharmony_ci  // There are two cases to consider:
791cb0ef41Sopenharmony_ci  //
801cb0ef41Sopenharmony_ci  //   1.  Any error raised after we have encountered the 'eof' token.
811cb0ef41Sopenharmony_ci  //       This prevents us from declaring partial tokens (like '2e') as
821cb0ef41Sopenharmony_ci  //       recoverable.
831cb0ef41Sopenharmony_ci  //
841cb0ef41Sopenharmony_ci  //   2.  Three cases where tokens can legally span lines.  This is
851cb0ef41Sopenharmony_ci  //       template, comment, and strings with a backslash at the end of
861cb0ef41Sopenharmony_ci  //       the line, indicating a continuation.  Note that we need to look
871cb0ef41Sopenharmony_ci  //       for the specific errors of 'unterminated' kind (not, for example,
881cb0ef41Sopenharmony_ci  //       a syntax error in a ${} expression in a template), and the only
891cb0ef41Sopenharmony_ci  //       way to do that currently is to look at the message.  Should Acorn
901cb0ef41Sopenharmony_ci  //       change these messages in the future, this will lead to a test
911cb0ef41Sopenharmony_ci  //       failure, indicating that this code needs to be updated.
921cb0ef41Sopenharmony_ci  //
931cb0ef41Sopenharmony_ci  const RecoverableParser = AcornParser
941cb0ef41Sopenharmony_ci    .extend(
951cb0ef41Sopenharmony_ci      (Parser) => {
961cb0ef41Sopenharmony_ci        return class extends Parser {
971cb0ef41Sopenharmony_ci          // eslint-disable-next-line no-useless-constructor
981cb0ef41Sopenharmony_ci          constructor(options, input, startPos) {
991cb0ef41Sopenharmony_ci            super(options, input, startPos);
1001cb0ef41Sopenharmony_ci          }
1011cb0ef41Sopenharmony_ci          nextToken() {
1021cb0ef41Sopenharmony_ci            super.nextToken();
1031cb0ef41Sopenharmony_ci            if (this.type === tt.eof)
1041cb0ef41Sopenharmony_ci              recoverable = true;
1051cb0ef41Sopenharmony_ci          }
1061cb0ef41Sopenharmony_ci          raise(pos, message) {
1071cb0ef41Sopenharmony_ci            switch (message) {
1081cb0ef41Sopenharmony_ci              case 'Unterminated template':
1091cb0ef41Sopenharmony_ci              case 'Unterminated comment':
1101cb0ef41Sopenharmony_ci                recoverable = true;
1111cb0ef41Sopenharmony_ci                break;
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci              case 'Unterminated string constant': {
1141cb0ef41Sopenharmony_ci                const token = StringPrototypeSlice(this.input,
1151cb0ef41Sopenharmony_ci                                                   this.lastTokStart, this.pos);
1161cb0ef41Sopenharmony_ci                // See https://www.ecma-international.org/ecma-262/#sec-line-terminators
1171cb0ef41Sopenharmony_ci                if (RegExpPrototypeExec(/\\(?:\r\n?|\n|\u2028|\u2029)$/,
1181cb0ef41Sopenharmony_ci                                        token) !== null) {
1191cb0ef41Sopenharmony_ci                  recoverable = true;
1201cb0ef41Sopenharmony_ci                }
1211cb0ef41Sopenharmony_ci              }
1221cb0ef41Sopenharmony_ci            }
1231cb0ef41Sopenharmony_ci            super.raise(pos, message);
1241cb0ef41Sopenharmony_ci          }
1251cb0ef41Sopenharmony_ci        };
1261cb0ef41Sopenharmony_ci      },
1271cb0ef41Sopenharmony_ci    );
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  // Try to parse the code with acorn.  If the parse fails, ignore the acorn
1301cb0ef41Sopenharmony_ci  // error and return the recoverable status.
1311cb0ef41Sopenharmony_ci  try {
1321cb0ef41Sopenharmony_ci    RecoverableParser.parse(code, { ecmaVersion: 'latest' });
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci    // Odd case: the underlying JS engine (V8, Chakra) rejected this input
1351cb0ef41Sopenharmony_ci    // but Acorn detected no issue.  Presume that additional text won't
1361cb0ef41Sopenharmony_ci    // address this issue.
1371cb0ef41Sopenharmony_ci    return false;
1381cb0ef41Sopenharmony_ci  } catch {
1391cb0ef41Sopenharmony_ci    return recoverable;
1401cb0ef41Sopenharmony_ci  }
1411cb0ef41Sopenharmony_ci}
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_cifunction setupPreview(repl, contextSymbol, bufferSymbol, active) {
1441cb0ef41Sopenharmony_ci  // Simple terminals can't handle previews.
1451cb0ef41Sopenharmony_ci  if (process.env.TERM === 'dumb' || !active) {
1461cb0ef41Sopenharmony_ci    return { showPreview() {}, clearPreview() {} };
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci  let inputPreview = null;
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci  let previewCompletionCounter = 0;
1521cb0ef41Sopenharmony_ci  let completionPreview = null;
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ci  let hasCompletions = false;
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci  let wrapped = false;
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci  let escaped = null;
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ci  function getPreviewPos() {
1611cb0ef41Sopenharmony_ci    const displayPos = repl._getDisplayPos(`${repl.getPrompt()}${repl.line}`);
1621cb0ef41Sopenharmony_ci    const cursorPos = repl.line.length !== repl.cursor ?
1631cb0ef41Sopenharmony_ci      repl.getCursorPos() :
1641cb0ef41Sopenharmony_ci      displayPos;
1651cb0ef41Sopenharmony_ci    return { displayPos, cursorPos };
1661cb0ef41Sopenharmony_ci  }
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci  function isCursorAtInputEnd() {
1691cb0ef41Sopenharmony_ci    const { cursorPos, displayPos } = getPreviewPos();
1701cb0ef41Sopenharmony_ci    return cursorPos.rows === displayPos.rows &&
1711cb0ef41Sopenharmony_ci           cursorPos.cols === displayPos.cols;
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  const clearPreview = (key) => {
1751cb0ef41Sopenharmony_ci    if (inputPreview !== null) {
1761cb0ef41Sopenharmony_ci      const { displayPos, cursorPos } = getPreviewPos();
1771cb0ef41Sopenharmony_ci      const rows = displayPos.rows - cursorPos.rows + 1;
1781cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, rows);
1791cb0ef41Sopenharmony_ci      clearLine(repl.output);
1801cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, -rows);
1811cb0ef41Sopenharmony_ci      inputPreview = null;
1821cb0ef41Sopenharmony_ci    }
1831cb0ef41Sopenharmony_ci    if (completionPreview !== null) {
1841cb0ef41Sopenharmony_ci      // Prevent cursor moves if not necessary!
1851cb0ef41Sopenharmony_ci      const move = repl.line.length !== repl.cursor;
1861cb0ef41Sopenharmony_ci      let pos, rows;
1871cb0ef41Sopenharmony_ci      if (move) {
1881cb0ef41Sopenharmony_ci        pos = getPreviewPos();
1891cb0ef41Sopenharmony_ci        cursorTo(repl.output, pos.displayPos.cols);
1901cb0ef41Sopenharmony_ci        rows = pos.displayPos.rows - pos.cursorPos.rows;
1911cb0ef41Sopenharmony_ci        moveCursor(repl.output, 0, rows);
1921cb0ef41Sopenharmony_ci      }
1931cb0ef41Sopenharmony_ci      const totalLine = `${repl.getPrompt()}${repl.line}${completionPreview}`;
1941cb0ef41Sopenharmony_ci      const newPos = repl._getDisplayPos(totalLine);
1951cb0ef41Sopenharmony_ci      // Minimize work for the terminal. It is enough to clear the right part of
1961cb0ef41Sopenharmony_ci      // the current line in case the preview is visible on a single line.
1971cb0ef41Sopenharmony_ci      if (newPos.rows === 0 || (pos && pos.displayPos.rows === newPos.rows)) {
1981cb0ef41Sopenharmony_ci        clearLine(repl.output, 1);
1991cb0ef41Sopenharmony_ci      } else {
2001cb0ef41Sopenharmony_ci        clearScreenDown(repl.output);
2011cb0ef41Sopenharmony_ci      }
2021cb0ef41Sopenharmony_ci      if (move) {
2031cb0ef41Sopenharmony_ci        cursorTo(repl.output, pos.cursorPos.cols);
2041cb0ef41Sopenharmony_ci        moveCursor(repl.output, 0, -rows);
2051cb0ef41Sopenharmony_ci      }
2061cb0ef41Sopenharmony_ci      if (!key.ctrl && !key.shift) {
2071cb0ef41Sopenharmony_ci        if (key.name === 'escape') {
2081cb0ef41Sopenharmony_ci          if (escaped === null && key.meta) {
2091cb0ef41Sopenharmony_ci            escaped = repl.line;
2101cb0ef41Sopenharmony_ci          }
2111cb0ef41Sopenharmony_ci        } else if ((key.name === 'return' || key.name === 'enter') &&
2121cb0ef41Sopenharmony_ci                   !key.meta &&
2131cb0ef41Sopenharmony_ci                   escaped !== repl.line &&
2141cb0ef41Sopenharmony_ci                   isCursorAtInputEnd()) {
2151cb0ef41Sopenharmony_ci          repl._insertString(completionPreview);
2161cb0ef41Sopenharmony_ci        }
2171cb0ef41Sopenharmony_ci      }
2181cb0ef41Sopenharmony_ci      completionPreview = null;
2191cb0ef41Sopenharmony_ci    }
2201cb0ef41Sopenharmony_ci    if (escaped !== repl.line) {
2211cb0ef41Sopenharmony_ci      escaped = null;
2221cb0ef41Sopenharmony_ci    }
2231cb0ef41Sopenharmony_ci  };
2241cb0ef41Sopenharmony_ci
2251cb0ef41Sopenharmony_ci  function showCompletionPreview(line, insertPreview) {
2261cb0ef41Sopenharmony_ci    previewCompletionCounter++;
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci    const count = previewCompletionCounter;
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ci    repl.completer(line, (error, data) => {
2311cb0ef41Sopenharmony_ci      // Tab completion might be async and the result might already be outdated.
2321cb0ef41Sopenharmony_ci      if (count !== previewCompletionCounter) {
2331cb0ef41Sopenharmony_ci        return;
2341cb0ef41Sopenharmony_ci      }
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci      if (error) {
2371cb0ef41Sopenharmony_ci        debug('Error while generating completion preview', error);
2381cb0ef41Sopenharmony_ci        return;
2391cb0ef41Sopenharmony_ci      }
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci      // Result and the text that was completed.
2421cb0ef41Sopenharmony_ci      const { 0: rawCompletions, 1: completeOn } = data;
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci      if (!rawCompletions || rawCompletions.length === 0) {
2451cb0ef41Sopenharmony_ci        return;
2461cb0ef41Sopenharmony_ci      }
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ci      hasCompletions = true;
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci      // If there is a common prefix to all matches, then apply that portion.
2511cb0ef41Sopenharmony_ci      const completions = ArrayPrototypeFilter(rawCompletions, Boolean);
2521cb0ef41Sopenharmony_ci      const prefix = commonPrefix(completions);
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_ci      // No common prefix found.
2551cb0ef41Sopenharmony_ci      if (prefix.length <= completeOn.length) {
2561cb0ef41Sopenharmony_ci        return;
2571cb0ef41Sopenharmony_ci      }
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci      const suffix = StringPrototypeSlice(prefix, completeOn.length);
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_ci      if (insertPreview) {
2621cb0ef41Sopenharmony_ci        repl._insertString(suffix);
2631cb0ef41Sopenharmony_ci        return;
2641cb0ef41Sopenharmony_ci      }
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci      completionPreview = suffix;
2671cb0ef41Sopenharmony_ci
2681cb0ef41Sopenharmony_ci      const result = repl.useColors ?
2691cb0ef41Sopenharmony_ci        `\u001b[90m${suffix}\u001b[39m` :
2701cb0ef41Sopenharmony_ci        ` // ${suffix}`;
2711cb0ef41Sopenharmony_ci
2721cb0ef41Sopenharmony_ci      const { cursorPos, displayPos } = getPreviewPos();
2731cb0ef41Sopenharmony_ci      if (repl.line.length !== repl.cursor) {
2741cb0ef41Sopenharmony_ci        cursorTo(repl.output, displayPos.cols);
2751cb0ef41Sopenharmony_ci        moveCursor(repl.output, 0, displayPos.rows - cursorPos.rows);
2761cb0ef41Sopenharmony_ci      }
2771cb0ef41Sopenharmony_ci      repl.output.write(result);
2781cb0ef41Sopenharmony_ci      cursorTo(repl.output, cursorPos.cols);
2791cb0ef41Sopenharmony_ci      const totalLine = `${repl.getPrompt()}${repl.line}${suffix}`;
2801cb0ef41Sopenharmony_ci      const newPos = repl._getDisplayPos(totalLine);
2811cb0ef41Sopenharmony_ci      const rows = newPos.rows - cursorPos.rows - (newPos.cols === 0 ? 1 : 0);
2821cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, -rows);
2831cb0ef41Sopenharmony_ci    });
2841cb0ef41Sopenharmony_ci  }
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ci  function isInStrictMode(repl) {
2871cb0ef41Sopenharmony_ci    return repl.replMode === REPL_MODE_STRICT || ArrayPrototypeIncludes(
2881cb0ef41Sopenharmony_ci      ArrayPrototypeMap(process.execArgv,
2891cb0ef41Sopenharmony_ci                        (e) => StringPrototypeReplaceAll(
2901cb0ef41Sopenharmony_ci                          StringPrototypeToLowerCase(e),
2911cb0ef41Sopenharmony_ci                          '_',
2921cb0ef41Sopenharmony_ci                          '-',
2931cb0ef41Sopenharmony_ci                        )),
2941cb0ef41Sopenharmony_ci      '--use-strict');
2951cb0ef41Sopenharmony_ci  }
2961cb0ef41Sopenharmony_ci
2971cb0ef41Sopenharmony_ci  // This returns a code preview for arbitrary input code.
2981cb0ef41Sopenharmony_ci  function getInputPreview(input, callback) {
2991cb0ef41Sopenharmony_ci    // For similar reasons as `defaultEval`, wrap expressions starting with a
3001cb0ef41Sopenharmony_ci    // curly brace with parenthesis.
3011cb0ef41Sopenharmony_ci    if (StringPrototypeStartsWith(input, '{') &&
3021cb0ef41Sopenharmony_ci        !StringPrototypeEndsWith(input, ';') && !wrapped) {
3031cb0ef41Sopenharmony_ci      input = `(${input})`;
3041cb0ef41Sopenharmony_ci      wrapped = true;
3051cb0ef41Sopenharmony_ci    }
3061cb0ef41Sopenharmony_ci    sendInspectorCommand((session) => {
3071cb0ef41Sopenharmony_ci      session.post('Runtime.evaluate', {
3081cb0ef41Sopenharmony_ci        expression: input,
3091cb0ef41Sopenharmony_ci        throwOnSideEffect: true,
3101cb0ef41Sopenharmony_ci        timeout: 333,
3111cb0ef41Sopenharmony_ci        contextId: repl[contextSymbol],
3121cb0ef41Sopenharmony_ci      }, (error, preview) => {
3131cb0ef41Sopenharmony_ci        if (error) {
3141cb0ef41Sopenharmony_ci          callback(error);
3151cb0ef41Sopenharmony_ci          return;
3161cb0ef41Sopenharmony_ci        }
3171cb0ef41Sopenharmony_ci        const { result } = preview;
3181cb0ef41Sopenharmony_ci        if (result.value !== undefined) {
3191cb0ef41Sopenharmony_ci          callback(null, inspect(result.value, previewOptions));
3201cb0ef41Sopenharmony_ci        // Ignore EvalErrors, SyntaxErrors and ReferenceErrors. It is not clear
3211cb0ef41Sopenharmony_ci        // where they came from and if they are recoverable or not. Other errors
3221cb0ef41Sopenharmony_ci        // may be inspected.
3231cb0ef41Sopenharmony_ci        } else if (preview.exceptionDetails &&
3241cb0ef41Sopenharmony_ci                   (result.className === 'EvalError' ||
3251cb0ef41Sopenharmony_ci                    result.className === 'SyntaxError' ||
3261cb0ef41Sopenharmony_ci                    // Report ReferenceError in case the strict mode is active
3271cb0ef41Sopenharmony_ci                    // for input that has no completions.
3281cb0ef41Sopenharmony_ci                    (result.className === 'ReferenceError' &&
3291cb0ef41Sopenharmony_ci                     (hasCompletions || !isInStrictMode(repl))))) {
3301cb0ef41Sopenharmony_ci          callback(null, null);
3311cb0ef41Sopenharmony_ci        } else if (result.objectId) {
3321cb0ef41Sopenharmony_ci          // The writer options might change and have influence on the inspect
3331cb0ef41Sopenharmony_ci          // output. The user might change e.g., `showProxy`, `getters` or
3341cb0ef41Sopenharmony_ci          // `showHidden`. Use `inspect` instead of `JSON.stringify` to keep
3351cb0ef41Sopenharmony_ci          // `Infinity` and similar intact.
3361cb0ef41Sopenharmony_ci          const inspectOptions = inspect({
3371cb0ef41Sopenharmony_ci            ...repl.writer.options,
3381cb0ef41Sopenharmony_ci            colors: false,
3391cb0ef41Sopenharmony_ci            depth: 1,
3401cb0ef41Sopenharmony_ci            compact: true,
3411cb0ef41Sopenharmony_ci            breakLength: Infinity,
3421cb0ef41Sopenharmony_ci          }, previewOptions);
3431cb0ef41Sopenharmony_ci          session.post('Runtime.callFunctionOn', {
3441cb0ef41Sopenharmony_ci            functionDeclaration:
3451cb0ef41Sopenharmony_ci              `(v) =>
3461cb0ef41Sopenharmony_ci                    Reflect
3471cb0ef41Sopenharmony_ci                    .getOwnPropertyDescriptor(globalThis, 'util')
3481cb0ef41Sopenharmony_ci                    .get().inspect(v, ${inspectOptions})`,
3491cb0ef41Sopenharmony_ci            objectId: result.objectId,
3501cb0ef41Sopenharmony_ci            arguments: [result],
3511cb0ef41Sopenharmony_ci          }, (error, preview) => {
3521cb0ef41Sopenharmony_ci            if (error) {
3531cb0ef41Sopenharmony_ci              callback(error);
3541cb0ef41Sopenharmony_ci            } else {
3551cb0ef41Sopenharmony_ci              callback(null, preview.result.value);
3561cb0ef41Sopenharmony_ci            }
3571cb0ef41Sopenharmony_ci          });
3581cb0ef41Sopenharmony_ci        } else {
3591cb0ef41Sopenharmony_ci          // Either not serializable or undefined.
3601cb0ef41Sopenharmony_ci          callback(null, result.unserializableValue || result.type);
3611cb0ef41Sopenharmony_ci        }
3621cb0ef41Sopenharmony_ci      });
3631cb0ef41Sopenharmony_ci    }, () => callback(new ERR_INSPECTOR_NOT_AVAILABLE()));
3641cb0ef41Sopenharmony_ci  }
3651cb0ef41Sopenharmony_ci
3661cb0ef41Sopenharmony_ci  const showPreview = (showCompletion = true) => {
3671cb0ef41Sopenharmony_ci    // Prevent duplicated previews after a refresh.
3681cb0ef41Sopenharmony_ci    if (inputPreview !== null || !repl.isCompletionEnabled) {
3691cb0ef41Sopenharmony_ci      return;
3701cb0ef41Sopenharmony_ci    }
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci    const line = StringPrototypeTrim(repl.line);
3731cb0ef41Sopenharmony_ci
3741cb0ef41Sopenharmony_ci    // Do not preview in case the line only contains whitespace.
3751cb0ef41Sopenharmony_ci    if (line === '') {
3761cb0ef41Sopenharmony_ci      return;
3771cb0ef41Sopenharmony_ci    }
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci    hasCompletions = false;
3801cb0ef41Sopenharmony_ci
3811cb0ef41Sopenharmony_ci    // Add the autocompletion preview.
3821cb0ef41Sopenharmony_ci    if (showCompletion) {
3831cb0ef41Sopenharmony_ci      const insertPreview = false;
3841cb0ef41Sopenharmony_ci      showCompletionPreview(repl.line, insertPreview);
3851cb0ef41Sopenharmony_ci    }
3861cb0ef41Sopenharmony_ci
3871cb0ef41Sopenharmony_ci    // Do not preview if the command is buffered.
3881cb0ef41Sopenharmony_ci    if (repl[bufferSymbol]) {
3891cb0ef41Sopenharmony_ci      return;
3901cb0ef41Sopenharmony_ci    }
3911cb0ef41Sopenharmony_ci
3921cb0ef41Sopenharmony_ci    const inputPreviewCallback = (error, inspected) => {
3931cb0ef41Sopenharmony_ci      if (inspected == null) {
3941cb0ef41Sopenharmony_ci        return;
3951cb0ef41Sopenharmony_ci      }
3961cb0ef41Sopenharmony_ci
3971cb0ef41Sopenharmony_ci      wrapped = false;
3981cb0ef41Sopenharmony_ci
3991cb0ef41Sopenharmony_ci      // Ignore the output if the value is identical to the current line.
4001cb0ef41Sopenharmony_ci      if (line === inspected) {
4011cb0ef41Sopenharmony_ci        return;
4021cb0ef41Sopenharmony_ci      }
4031cb0ef41Sopenharmony_ci
4041cb0ef41Sopenharmony_ci      if (error) {
4051cb0ef41Sopenharmony_ci        debug('Error while generating preview', error);
4061cb0ef41Sopenharmony_ci        return;
4071cb0ef41Sopenharmony_ci      }
4081cb0ef41Sopenharmony_ci      // Do not preview `undefined` if colors are deactivated or explicitly
4091cb0ef41Sopenharmony_ci      // requested.
4101cb0ef41Sopenharmony_ci      if (inspected === 'undefined' &&
4111cb0ef41Sopenharmony_ci          (!repl.useColors || repl.ignoreUndefined)) {
4121cb0ef41Sopenharmony_ci        return;
4131cb0ef41Sopenharmony_ci      }
4141cb0ef41Sopenharmony_ci
4151cb0ef41Sopenharmony_ci      inputPreview = inspected;
4161cb0ef41Sopenharmony_ci
4171cb0ef41Sopenharmony_ci      // Limit the output to maximum 250 characters. Otherwise it becomes a)
4181cb0ef41Sopenharmony_ci      // difficult to read and b) non terminal REPLs would visualize the whole
4191cb0ef41Sopenharmony_ci      // output.
4201cb0ef41Sopenharmony_ci      let maxColumns = MathMin(repl.columns, 250);
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_ci      // Support unicode characters of width other than one by checking the
4231cb0ef41Sopenharmony_ci      // actual width.
4241cb0ef41Sopenharmony_ci      if (inspected.length * 2 >= maxColumns &&
4251cb0ef41Sopenharmony_ci          getStringWidth(inspected) > maxColumns) {
4261cb0ef41Sopenharmony_ci        maxColumns -= 4 + (repl.useColors ? 0 : 3);
4271cb0ef41Sopenharmony_ci        let res = '';
4281cb0ef41Sopenharmony_ci        for (const char of new SafeStringIterator(inspected)) {
4291cb0ef41Sopenharmony_ci          maxColumns -= getStringWidth(char);
4301cb0ef41Sopenharmony_ci          if (maxColumns < 0)
4311cb0ef41Sopenharmony_ci            break;
4321cb0ef41Sopenharmony_ci          res += char;
4331cb0ef41Sopenharmony_ci        }
4341cb0ef41Sopenharmony_ci        inspected = `${res}...`;
4351cb0ef41Sopenharmony_ci      }
4361cb0ef41Sopenharmony_ci
4371cb0ef41Sopenharmony_ci      // Line breaks are very rare and probably only occur in case of error
4381cb0ef41Sopenharmony_ci      // messages with line breaks.
4391cb0ef41Sopenharmony_ci      const lineBreakPos = StringPrototypeIndexOf(inspected, '\n');
4401cb0ef41Sopenharmony_ci      if (lineBreakPos !== -1) {
4411cb0ef41Sopenharmony_ci        inspected = `${StringPrototypeSlice(inspected, 0, lineBreakPos)}`;
4421cb0ef41Sopenharmony_ci      }
4431cb0ef41Sopenharmony_ci
4441cb0ef41Sopenharmony_ci      const result = repl.useColors ?
4451cb0ef41Sopenharmony_ci        `\u001b[90m${inspected}\u001b[39m` :
4461cb0ef41Sopenharmony_ci        `// ${inspected}`;
4471cb0ef41Sopenharmony_ci
4481cb0ef41Sopenharmony_ci      const { cursorPos, displayPos } = getPreviewPos();
4491cb0ef41Sopenharmony_ci      const rows = displayPos.rows - cursorPos.rows;
4501cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, rows);
4511cb0ef41Sopenharmony_ci      repl.output.write(`\n${result}`);
4521cb0ef41Sopenharmony_ci      cursorTo(repl.output, cursorPos.cols);
4531cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, -rows - 1);
4541cb0ef41Sopenharmony_ci    };
4551cb0ef41Sopenharmony_ci
4561cb0ef41Sopenharmony_ci    let previewLine = line;
4571cb0ef41Sopenharmony_ci
4581cb0ef41Sopenharmony_ci    if (completionPreview !== null &&
4591cb0ef41Sopenharmony_ci        isCursorAtInputEnd() &&
4601cb0ef41Sopenharmony_ci        escaped !== repl.line) {
4611cb0ef41Sopenharmony_ci      previewLine += completionPreview;
4621cb0ef41Sopenharmony_ci    }
4631cb0ef41Sopenharmony_ci
4641cb0ef41Sopenharmony_ci    getInputPreview(previewLine, inputPreviewCallback);
4651cb0ef41Sopenharmony_ci    if (wrapped) {
4661cb0ef41Sopenharmony_ci      getInputPreview(previewLine, inputPreviewCallback);
4671cb0ef41Sopenharmony_ci    }
4681cb0ef41Sopenharmony_ci    wrapped = false;
4691cb0ef41Sopenharmony_ci  };
4701cb0ef41Sopenharmony_ci
4711cb0ef41Sopenharmony_ci  // -------------------------------------------------------------------------//
4721cb0ef41Sopenharmony_ci  // Replace multiple interface functions. This is required to fully support  //
4731cb0ef41Sopenharmony_ci  // previews without changing readlines behavior.                            //
4741cb0ef41Sopenharmony_ci  // -------------------------------------------------------------------------//
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ci  // Refresh prints the whole screen again and the preview will be removed
4771cb0ef41Sopenharmony_ci  // during that procedure. Print the preview again. This also makes sure
4781cb0ef41Sopenharmony_ci  // the preview is always correct after resizing the terminal window.
4791cb0ef41Sopenharmony_ci  const originalRefresh = FunctionPrototypeBind(repl._refreshLine, repl);
4801cb0ef41Sopenharmony_ci  repl._refreshLine = () => {
4811cb0ef41Sopenharmony_ci    inputPreview = null;
4821cb0ef41Sopenharmony_ci    originalRefresh();
4831cb0ef41Sopenharmony_ci    showPreview();
4841cb0ef41Sopenharmony_ci  };
4851cb0ef41Sopenharmony_ci
4861cb0ef41Sopenharmony_ci  let insertCompletionPreview = true;
4871cb0ef41Sopenharmony_ci  // Insert the longest common suffix of the current input in case the user
4881cb0ef41Sopenharmony_ci  // moves to the right while already being at the current input end.
4891cb0ef41Sopenharmony_ci  const originalMoveCursor = FunctionPrototypeBind(repl._moveCursor, repl);
4901cb0ef41Sopenharmony_ci  repl._moveCursor = (dx) => {
4911cb0ef41Sopenharmony_ci    const currentCursor = repl.cursor;
4921cb0ef41Sopenharmony_ci    originalMoveCursor(dx);
4931cb0ef41Sopenharmony_ci    if (currentCursor + dx > repl.line.length &&
4941cb0ef41Sopenharmony_ci        typeof repl.completer === 'function' &&
4951cb0ef41Sopenharmony_ci        insertCompletionPreview) {
4961cb0ef41Sopenharmony_ci      const insertPreview = true;
4971cb0ef41Sopenharmony_ci      showCompletionPreview(repl.line, insertPreview);
4981cb0ef41Sopenharmony_ci    }
4991cb0ef41Sopenharmony_ci  };
5001cb0ef41Sopenharmony_ci
5011cb0ef41Sopenharmony_ci  // This is the only function that interferes with the completion insertion.
5021cb0ef41Sopenharmony_ci  // Monkey patch it to prevent inserting the completion when it shouldn't be.
5031cb0ef41Sopenharmony_ci  const originalClearLine = FunctionPrototypeBind(repl.clearLine, repl);
5041cb0ef41Sopenharmony_ci  repl.clearLine = () => {
5051cb0ef41Sopenharmony_ci    insertCompletionPreview = false;
5061cb0ef41Sopenharmony_ci    originalClearLine();
5071cb0ef41Sopenharmony_ci    insertCompletionPreview = true;
5081cb0ef41Sopenharmony_ci  };
5091cb0ef41Sopenharmony_ci
5101cb0ef41Sopenharmony_ci  return { showPreview, clearPreview };
5111cb0ef41Sopenharmony_ci}
5121cb0ef41Sopenharmony_ci
5131cb0ef41Sopenharmony_cifunction setupReverseSearch(repl) {
5141cb0ef41Sopenharmony_ci  // Simple terminals can't use reverse search.
5151cb0ef41Sopenharmony_ci  if (process.env.TERM === 'dumb') {
5161cb0ef41Sopenharmony_ci    return { reverseSearch() { return false; } };
5171cb0ef41Sopenharmony_ci  }
5181cb0ef41Sopenharmony_ci
5191cb0ef41Sopenharmony_ci  const alreadyMatched = new SafeSet();
5201cb0ef41Sopenharmony_ci  const labels = {
5211cb0ef41Sopenharmony_ci    r: 'bck-i-search: ',
5221cb0ef41Sopenharmony_ci    s: 'fwd-i-search: ',
5231cb0ef41Sopenharmony_ci  };
5241cb0ef41Sopenharmony_ci  let isInReverseSearch = false;
5251cb0ef41Sopenharmony_ci  let historyIndex = -1;
5261cb0ef41Sopenharmony_ci  let input = '';
5271cb0ef41Sopenharmony_ci  let cursor = -1;
5281cb0ef41Sopenharmony_ci  let dir = 'r';
5291cb0ef41Sopenharmony_ci  let lastMatch = -1;
5301cb0ef41Sopenharmony_ci  let lastCursor = -1;
5311cb0ef41Sopenharmony_ci  let promptPos;
5321cb0ef41Sopenharmony_ci
5331cb0ef41Sopenharmony_ci  function checkAndSetDirectionKey(keyName) {
5341cb0ef41Sopenharmony_ci    if (!labels[keyName]) {
5351cb0ef41Sopenharmony_ci      return false;
5361cb0ef41Sopenharmony_ci    }
5371cb0ef41Sopenharmony_ci    if (dir !== keyName) {
5381cb0ef41Sopenharmony_ci      // Reset the already matched set in case the direction is changed. That
5391cb0ef41Sopenharmony_ci      // way it's possible to find those entries again.
5401cb0ef41Sopenharmony_ci      alreadyMatched.clear();
5411cb0ef41Sopenharmony_ci      dir = keyName;
5421cb0ef41Sopenharmony_ci    }
5431cb0ef41Sopenharmony_ci    return true;
5441cb0ef41Sopenharmony_ci  }
5451cb0ef41Sopenharmony_ci
5461cb0ef41Sopenharmony_ci  function goToNextHistoryIndex() {
5471cb0ef41Sopenharmony_ci    // Ignore this entry for further searches and continue to the next
5481cb0ef41Sopenharmony_ci    // history entry.
5491cb0ef41Sopenharmony_ci    alreadyMatched.add(repl.history[historyIndex]);
5501cb0ef41Sopenharmony_ci    historyIndex += dir === 'r' ? 1 : -1;
5511cb0ef41Sopenharmony_ci    cursor = -1;
5521cb0ef41Sopenharmony_ci  }
5531cb0ef41Sopenharmony_ci
5541cb0ef41Sopenharmony_ci  function search() {
5551cb0ef41Sopenharmony_ci    // Just print an empty line in case the user removed the search parameter.
5561cb0ef41Sopenharmony_ci    if (input === '') {
5571cb0ef41Sopenharmony_ci      print(repl.line, `${labels[dir]}_`);
5581cb0ef41Sopenharmony_ci      return;
5591cb0ef41Sopenharmony_ci    }
5601cb0ef41Sopenharmony_ci    // Fix the bounds in case the direction has changed in the meanwhile.
5611cb0ef41Sopenharmony_ci    if (dir === 'r') {
5621cb0ef41Sopenharmony_ci      if (historyIndex < 0) {
5631cb0ef41Sopenharmony_ci        historyIndex = 0;
5641cb0ef41Sopenharmony_ci      }
5651cb0ef41Sopenharmony_ci    } else if (historyIndex >= repl.history.length) {
5661cb0ef41Sopenharmony_ci      historyIndex = repl.history.length - 1;
5671cb0ef41Sopenharmony_ci    }
5681cb0ef41Sopenharmony_ci    // Check the history entries until a match is found.
5691cb0ef41Sopenharmony_ci    while (historyIndex >= 0 && historyIndex < repl.history.length) {
5701cb0ef41Sopenharmony_ci      let entry = repl.history[historyIndex];
5711cb0ef41Sopenharmony_ci      // Visualize all potential matches only once.
5721cb0ef41Sopenharmony_ci      if (alreadyMatched.has(entry)) {
5731cb0ef41Sopenharmony_ci        historyIndex += dir === 'r' ? 1 : -1;
5741cb0ef41Sopenharmony_ci        continue;
5751cb0ef41Sopenharmony_ci      }
5761cb0ef41Sopenharmony_ci      // Match the next entry either from the start or from the end, depending
5771cb0ef41Sopenharmony_ci      // on the current direction.
5781cb0ef41Sopenharmony_ci      if (dir === 'r') {
5791cb0ef41Sopenharmony_ci        // Update the cursor in case it's necessary.
5801cb0ef41Sopenharmony_ci        if (cursor === -1) {
5811cb0ef41Sopenharmony_ci          cursor = entry.length;
5821cb0ef41Sopenharmony_ci        }
5831cb0ef41Sopenharmony_ci        cursor = StringPrototypeLastIndexOf(entry, input, cursor - 1);
5841cb0ef41Sopenharmony_ci      } else {
5851cb0ef41Sopenharmony_ci        cursor = StringPrototypeIndexOf(entry, input, cursor + 1);
5861cb0ef41Sopenharmony_ci      }
5871cb0ef41Sopenharmony_ci      // Match not found.
5881cb0ef41Sopenharmony_ci      if (cursor === -1) {
5891cb0ef41Sopenharmony_ci        goToNextHistoryIndex();
5901cb0ef41Sopenharmony_ci      // Match found.
5911cb0ef41Sopenharmony_ci      } else {
5921cb0ef41Sopenharmony_ci        if (repl.useColors) {
5931cb0ef41Sopenharmony_ci          const start = StringPrototypeSlice(entry, 0, cursor);
5941cb0ef41Sopenharmony_ci          const end = StringPrototypeSlice(entry, cursor + input.length);
5951cb0ef41Sopenharmony_ci          entry = `${start}\x1B[4m${input}\x1B[24m${end}`;
5961cb0ef41Sopenharmony_ci        }
5971cb0ef41Sopenharmony_ci        print(entry, `${labels[dir]}${input}_`, cursor);
5981cb0ef41Sopenharmony_ci        lastMatch = historyIndex;
5991cb0ef41Sopenharmony_ci        lastCursor = cursor;
6001cb0ef41Sopenharmony_ci        // Explicitly go to the next history item in case no further matches are
6011cb0ef41Sopenharmony_ci        // possible with the current entry.
6021cb0ef41Sopenharmony_ci        if ((dir === 'r' && cursor === 0) ||
6031cb0ef41Sopenharmony_ci            (dir === 's' && entry.length === cursor + input.length)) {
6041cb0ef41Sopenharmony_ci          goToNextHistoryIndex();
6051cb0ef41Sopenharmony_ci        }
6061cb0ef41Sopenharmony_ci        return;
6071cb0ef41Sopenharmony_ci      }
6081cb0ef41Sopenharmony_ci    }
6091cb0ef41Sopenharmony_ci    print(repl.line, `failed-${labels[dir]}${input}_`);
6101cb0ef41Sopenharmony_ci  }
6111cb0ef41Sopenharmony_ci
6121cb0ef41Sopenharmony_ci  function print(outputLine, inputLine, cursor = repl.cursor) {
6131cb0ef41Sopenharmony_ci    // TODO(BridgeAR): Resizing the terminal window hides the overlay. To fix
6141cb0ef41Sopenharmony_ci    // that, readline must be aware of this information. It's probably best to
6151cb0ef41Sopenharmony_ci    // add a couple of properties to readline that allow to do the following:
6161cb0ef41Sopenharmony_ci    // 1. Add arbitrary data to the end of the current line while not counting
6171cb0ef41Sopenharmony_ci    //    towards the line. This would be useful for the completion previews.
6181cb0ef41Sopenharmony_ci    // 2. Add arbitrary extra lines that do not count towards the regular line.
6191cb0ef41Sopenharmony_ci    //    This would be useful for both, the input preview and the reverse
6201cb0ef41Sopenharmony_ci    //    search. It might be combined with the first part?
6211cb0ef41Sopenharmony_ci    // 3. Add arbitrary input that is "on top" of the current line. That is
6221cb0ef41Sopenharmony_ci    //    useful for the reverse search.
6231cb0ef41Sopenharmony_ci    // 4. To trigger the line refresh, functions should be used to pass through
6241cb0ef41Sopenharmony_ci    //    the information. Alternatively, getters and setters could be used.
6251cb0ef41Sopenharmony_ci    //    That might even be more elegant.
6261cb0ef41Sopenharmony_ci    // The data would then be accounted for when calling `_refreshLine()`.
6271cb0ef41Sopenharmony_ci    // This function would then look similar to:
6281cb0ef41Sopenharmony_ci    //   repl.overlay(outputLine);
6291cb0ef41Sopenharmony_ci    //   repl.addTrailingLine(inputLine);
6301cb0ef41Sopenharmony_ci    //   repl.setCursor(cursor);
6311cb0ef41Sopenharmony_ci    // More potential improvements: use something similar to stream.cork().
6321cb0ef41Sopenharmony_ci    // Multiple cursor moves on the same tick could be prevented in case all
6331cb0ef41Sopenharmony_ci    // writes from the same tick are combined and the cursor is moved at the
6341cb0ef41Sopenharmony_ci    // tick end instead of after each operation.
6351cb0ef41Sopenharmony_ci    let rows = 0;
6361cb0ef41Sopenharmony_ci    if (lastMatch !== -1) {
6371cb0ef41Sopenharmony_ci      const line = StringPrototypeSlice(repl.history[lastMatch], 0, lastCursor);
6381cb0ef41Sopenharmony_ci      rows = repl._getDisplayPos(`${repl.getPrompt()}${line}`).rows;
6391cb0ef41Sopenharmony_ci      cursorTo(repl.output, promptPos.cols);
6401cb0ef41Sopenharmony_ci    } else if (isInReverseSearch && repl.line !== '') {
6411cb0ef41Sopenharmony_ci      rows = repl.getCursorPos().rows;
6421cb0ef41Sopenharmony_ci      cursorTo(repl.output, promptPos.cols);
6431cb0ef41Sopenharmony_ci    }
6441cb0ef41Sopenharmony_ci    if (rows !== 0)
6451cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, -rows);
6461cb0ef41Sopenharmony_ci
6471cb0ef41Sopenharmony_ci    if (isInReverseSearch) {
6481cb0ef41Sopenharmony_ci      clearScreenDown(repl.output);
6491cb0ef41Sopenharmony_ci      repl.output.write(`${outputLine}\n${inputLine}`);
6501cb0ef41Sopenharmony_ci    } else {
6511cb0ef41Sopenharmony_ci      repl.output.write(`\n${inputLine}`);
6521cb0ef41Sopenharmony_ci    }
6531cb0ef41Sopenharmony_ci
6541cb0ef41Sopenharmony_ci    lastMatch = -1;
6551cb0ef41Sopenharmony_ci
6561cb0ef41Sopenharmony_ci    // To know exactly how many rows we have to move the cursor back we need the
6571cb0ef41Sopenharmony_ci    // cursor rows, the output rows and the input rows.
6581cb0ef41Sopenharmony_ci    const prompt = repl.getPrompt();
6591cb0ef41Sopenharmony_ci    const cursorLine = prompt + StringPrototypeSlice(outputLine, 0, cursor);
6601cb0ef41Sopenharmony_ci    const cursorPos = repl._getDisplayPos(cursorLine);
6611cb0ef41Sopenharmony_ci    const outputPos = repl._getDisplayPos(`${prompt}${outputLine}`);
6621cb0ef41Sopenharmony_ci    const inputPos = repl._getDisplayPos(inputLine);
6631cb0ef41Sopenharmony_ci    const inputRows = inputPos.rows - (inputPos.cols === 0 ? 1 : 0);
6641cb0ef41Sopenharmony_ci
6651cb0ef41Sopenharmony_ci    rows = -1 - inputRows - (outputPos.rows - cursorPos.rows);
6661cb0ef41Sopenharmony_ci
6671cb0ef41Sopenharmony_ci    moveCursor(repl.output, 0, rows);
6681cb0ef41Sopenharmony_ci    cursorTo(repl.output, cursorPos.cols);
6691cb0ef41Sopenharmony_ci  }
6701cb0ef41Sopenharmony_ci
6711cb0ef41Sopenharmony_ci  function reset(string) {
6721cb0ef41Sopenharmony_ci    isInReverseSearch = string !== undefined;
6731cb0ef41Sopenharmony_ci
6741cb0ef41Sopenharmony_ci    // In case the reverse search ends and a history entry is found, reset the
6751cb0ef41Sopenharmony_ci    // line to the found entry.
6761cb0ef41Sopenharmony_ci    if (!isInReverseSearch) {
6771cb0ef41Sopenharmony_ci      if (lastMatch !== -1) {
6781cb0ef41Sopenharmony_ci        repl.line = repl.history[lastMatch];
6791cb0ef41Sopenharmony_ci        repl.cursor = lastCursor;
6801cb0ef41Sopenharmony_ci        repl.historyIndex = lastMatch;
6811cb0ef41Sopenharmony_ci      }
6821cb0ef41Sopenharmony_ci
6831cb0ef41Sopenharmony_ci      lastMatch = -1;
6841cb0ef41Sopenharmony_ci
6851cb0ef41Sopenharmony_ci      // Clear screen and write the current repl.line before exiting.
6861cb0ef41Sopenharmony_ci      cursorTo(repl.output, promptPos.cols);
6871cb0ef41Sopenharmony_ci      moveCursor(repl.output, 0, promptPos.rows);
6881cb0ef41Sopenharmony_ci      clearScreenDown(repl.output);
6891cb0ef41Sopenharmony_ci      if (repl.line !== '') {
6901cb0ef41Sopenharmony_ci        repl.output.write(repl.line);
6911cb0ef41Sopenharmony_ci        if (repl.line.length !== repl.cursor) {
6921cb0ef41Sopenharmony_ci          const { cols, rows } = repl.getCursorPos();
6931cb0ef41Sopenharmony_ci          cursorTo(repl.output, cols);
6941cb0ef41Sopenharmony_ci          moveCursor(repl.output, 0, rows);
6951cb0ef41Sopenharmony_ci        }
6961cb0ef41Sopenharmony_ci      }
6971cb0ef41Sopenharmony_ci    }
6981cb0ef41Sopenharmony_ci
6991cb0ef41Sopenharmony_ci    input = string || '';
7001cb0ef41Sopenharmony_ci    cursor = -1;
7011cb0ef41Sopenharmony_ci    historyIndex = repl.historyIndex;
7021cb0ef41Sopenharmony_ci    alreadyMatched.clear();
7031cb0ef41Sopenharmony_ci  }
7041cb0ef41Sopenharmony_ci
7051cb0ef41Sopenharmony_ci  function reverseSearch(string, key) {
7061cb0ef41Sopenharmony_ci    if (!isInReverseSearch) {
7071cb0ef41Sopenharmony_ci      if (key.ctrl && checkAndSetDirectionKey(key.name)) {
7081cb0ef41Sopenharmony_ci        historyIndex = repl.historyIndex;
7091cb0ef41Sopenharmony_ci        promptPos = repl._getDisplayPos(`${repl.getPrompt()}`);
7101cb0ef41Sopenharmony_ci        print(repl.line, `${labels[dir]}_`);
7111cb0ef41Sopenharmony_ci        isInReverseSearch = true;
7121cb0ef41Sopenharmony_ci      }
7131cb0ef41Sopenharmony_ci    } else if (key.ctrl && checkAndSetDirectionKey(key.name)) {
7141cb0ef41Sopenharmony_ci      search();
7151cb0ef41Sopenharmony_ci    } else if (key.name === 'backspace' ||
7161cb0ef41Sopenharmony_ci        (key.ctrl && (key.name === 'h' || key.name === 'w'))) {
7171cb0ef41Sopenharmony_ci      reset(StringPrototypeSlice(input, 0, input.length - 1));
7181cb0ef41Sopenharmony_ci      search();
7191cb0ef41Sopenharmony_ci      // Special handle <ctrl> + c and escape. Those should only cancel the
7201cb0ef41Sopenharmony_ci      // reverse search. The original line is visible afterwards again.
7211cb0ef41Sopenharmony_ci    } else if ((key.ctrl && key.name === 'c') || key.name === 'escape') {
7221cb0ef41Sopenharmony_ci      lastMatch = -1;
7231cb0ef41Sopenharmony_ci      reset();
7241cb0ef41Sopenharmony_ci      return true;
7251cb0ef41Sopenharmony_ci      // End search in case either enter is pressed or if any non-reverse-search
7261cb0ef41Sopenharmony_ci      // key (combination) is pressed.
7271cb0ef41Sopenharmony_ci    } else if (key.ctrl ||
7281cb0ef41Sopenharmony_ci               key.meta ||
7291cb0ef41Sopenharmony_ci               key.name === 'return' ||
7301cb0ef41Sopenharmony_ci               key.name === 'enter' ||
7311cb0ef41Sopenharmony_ci               typeof string !== 'string' ||
7321cb0ef41Sopenharmony_ci               string === '') {
7331cb0ef41Sopenharmony_ci      reset();
7341cb0ef41Sopenharmony_ci      repl[kSubstringSearch] = '';
7351cb0ef41Sopenharmony_ci    } else {
7361cb0ef41Sopenharmony_ci      reset(`${input}${string}`);
7371cb0ef41Sopenharmony_ci      search();
7381cb0ef41Sopenharmony_ci    }
7391cb0ef41Sopenharmony_ci    return isInReverseSearch;
7401cb0ef41Sopenharmony_ci  }
7411cb0ef41Sopenharmony_ci
7421cb0ef41Sopenharmony_ci  return { reverseSearch };
7431cb0ef41Sopenharmony_ci}
7441cb0ef41Sopenharmony_ci
7451cb0ef41Sopenharmony_cimodule.exports = {
7461cb0ef41Sopenharmony_ci  REPL_MODE_SLOPPY: Symbol('repl-sloppy'),
7471cb0ef41Sopenharmony_ci  REPL_MODE_STRICT,
7481cb0ef41Sopenharmony_ci  isRecoverableError,
7491cb0ef41Sopenharmony_ci  kStandaloneREPL: Symbol('kStandaloneREPL'),
7501cb0ef41Sopenharmony_ci  setupPreview,
7511cb0ef41Sopenharmony_ci  setupReverseSearch,
7521cb0ef41Sopenharmony_ci};
753