将Node REPL调整为不阻塞异步回调的提示



在使用Node REPL控制台时,我正在寻找一种异步调用的方法,既不锁定控制台输入,也不在返回时破坏控制台提示。

我在REPL中看到了一些处理异步调用的解决方案,在这里,编写自己的自定义eval函数,可以检查执行代码的结果,看看它是否是promise/async对象,只是在它解析之前不返回。

然而,我想解决一个重复的后台进程记录其进度的问题。理想情况下,我希望它的行为像Chrome Javascript控制台,如果你在控制台提示符上部分打印了一个命令,并且记录了一个异步结果,那么你键入的行就会下移一行,日志就会插入到它上面。

因此,从功能上讲,我想插入这样的逻辑:当调用console.log()时,首先清除包含光标的行,然后写入log()的内容,然后在新输出后的行上重写REPL提示(以及用户迄今为止在其上键入的任何内容)。

有没有任何方法可以挂接到REPL对象来实现这一点?这是对REPL输出流的某种高级操作吗(即,仅在支持"擦除行首"转义码的终端上可能)?

直接进入输出流,我成功地实现了我想要的工作:

var server = repl.start({
    prompt: '> '
});
server.context.console.log = function(msg) {
    var rli = server.rli;
    server.outputStream.write('33[2K33[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('33['+(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的padEnd,可以用任何lib替换

基于@MidnightLightning的旧答案。

在节点v12.15.0之前的某个时间,console.log覆盖看起来像:

activeRepl.context.console.log = (msg) => {
  const promptOffset = Server._prompt.length + Server.line.length;
  Server.outputStream.write('33[2K33[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('33[' + (promptOffset + 1) + 'G'); // Move the cursor to where it was
};

最新更新