Отрегулировать Node REPL, чтобы не вызывать всплывающее окно при асинхронном обратном вызове

При работе с консолью Node REPL я ищу способ асинхронных вызовов, чтобы не блокировать ввод с консоли и не загромождать приглашение консоли при их возврате.

Я видел несколько решений, связанных с асинхронными вызовами в REPL, где пишу свой собственный eval Функция позволяет проверить результат выполненного кода, чтобы увидеть, является ли он объектом обещания / асинхронности, и просто не возвращать, пока он не разрешится.

Тем не менее, я хотел бы решить проблему повторяющегося фонового процесса, регистрирующего его ход. В идеале я бы хотел, чтобы он вел себя как консоль Chrome Javascript, где, если у вас есть команда, частично напечатанная в командной строке, и в результате записывается асинхронный результат, строка, на которой вы печатаете, перемещается на одну строку вниз, а журнал вставлен над ним.

Итак, функционально, я хотел бы вставить логику, что когда console.log() вызывается, строка, содержащая курсор, сначала очищается, затем содержимое log() записывается, а затем приглашение REPL (и все, что пользователь уже набрал на нем) переписывается в строке после нового вывода.

Есть ли способ подключиться к объекту REPL для достижения этой цели? Является ли это каким-то продвинутым манипулированием выходным потоком из REPL (т. Е. Возможно только на терминалах, которые поддерживают escape-код "стирание в начало строки")?

3 ответа

Прямо в поток вывода мне удалось получить что-то вроде того, что я хотел:

var server = repl.start({
    prompt: '> '
});
server.context.console.log = function(msg) {
    var rli = server.rli;
    server.outputStream.write('\033[2K\033[1G'); // Erase to beginning of line, and reposition cursor at beginning of line
    server.outputStream.write(msg+"\n");
    server.outputStream.write(''+rli._prompt+rli.line); // Redraw existing line
    server.outputStream.write('\033['+(rli.cursor+rli._promptLength+1)+'G'); // Move the cursor to where it was
}

server.context.doTimeout = function() {
    setTimeout(function() {
        server.context.console.log('Timeout done!');
    }, 2000);
};

Несмотря на то, что все предполагает, что выходной поток соответствует ANSI, это вывод, и он выглядит довольно взломанным, переопределяя console.log() функционировать так. Есть ли более гибкий способ справиться с этим или это лучший способ?

Тем не менее, сейчас я не могу найти современного удовлетворительного ответа на StackOverflow. Проведя несколько часов в Интернете, я придумал это решение для последней версии Node (v15. *).

      const log = (msg) => {
  rl.output.write('\r')
  rl.output.write(msg + '\n')
  rl.displayPrompt(true)
}

Как правило, очистите строку, запишите журнал, а затем снова отобразите подсказку. Отлично работает в моем случае.

Улучшенное решение. Вышеупомянутое работает хорошо, если строка в буфере не длиннее журнала. Итак, вот еще одна версия, которая дополняет оставшуюся часть журнала пробелами

      const log = (msg) => {
  rl.output.write('\r');
  rl.output.write(_.padEnd(msg, process.stdout.columns - 2, ' ') + '\n');
  rl.displayPrompt(true);
};
  • _.padEnd: lodash's padEnd, можно заменить любой библиотекой

На основе более старого ответа @MidnightLightning.

Когда-то до узла v12.15.0 переопределение console.log выглядело бы так:

activeRepl.context.console.log = (msg) => {
  const promptOffset = Server._prompt.length + Server.line.length;
  Server.outputStream.write('\033[2K\033[1G'); // Erase to beginning of line, and reposition cursor at beginning of line
  Server.outputStream.write(msg + "\n");
  Server.outputStream.write('' + Server._prompt + Server.line); // Redraw existing line
  Server.outputStream.write('\033[' + (promptOffset + 1) + 'G'); // Move the cursor to where it was
};
Другие вопросы по тегам