Node: Readline printing output history while printing with readline.write

Created on 9 May 2017  路  3Comments  路  Source: nodejs/node

  • Version: v7.10.0
  • Platform: Linux Mint 18.1 64-bit and Windows 10 Professional 64-bit
  • Subsystem: Readline
var readline = require("readline");
var iface = new readline.Interface({
  input: process.stdin,
  output: process.stdout,
  prompt: ""
});

var i = 0;
var string = 'Loading';
var done = false;

// Once ctrl-c is hit, it sets done to true.
iface.once('SIGINT', () => { done = true; });

test();
function test() {
  // Clear the line, move the cursor to the beginning and print loading again
  readline.clearLine(process.stdout, 0);
  readline.moveCursor(process.stdout, (-string.length - 4), 0);
  iface.write(string);

  // print i dots
  for (var n = 0; n < i; n++) {
    iface.write('.');
  }

  if (done) {
    // Make sure there are 3 dots on the line
    for (var n = 0; n < 3 - i; n++) {
      iface.write('.');
    }
    // Show that program is done then close.
    iface.write('done\n');
    iface.close();
    return;
  } else {
    i = i < 3 ? i + 1 : 0; // Increment i if i is less than 3, otherwise set i to 0
    setTimeout(test, 1000);
  }
}

Code Objective:
Make a loop animating the printing of three dots to indicate that the computer is responsive and the app is not hanging. Loading is completed when the user presses Ctrl-C. While the current code isn't the optimal way of doing things, it outputs like so:

Loading (clear line) Loading. (clear line) Loading.. (clear line) Loading... (clear line) Loading (clear line)...

Issue:
This code works pretty well for the first minute or so then it will suddenly print out all of the output history all at once. However, the program will still continue the animation as expected. After a certain amount of time, it will print out the output history again.

Here is the output to one of those instances:

LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..
Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.
Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...Loading
Loading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...
LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..
Loading...LoadingLoading.Loading..Loading...LoadingLoading.Loading..Loading...LoadingLoading.
Loading..Loading...LoadingLoading

Workaround:
The issue was fixed when iface.write() is replaced with process.stdout.write(). This may have to do with how iface.write works. Unless you are doing something that requires iface.write(), it's probably best that you write directly to the stream object (IE. process.stdout.write()).

question readline

Most helpful comment

The problem is that readline.clearLine() and readline.moveCursor() functions operate on a raw TTY stream sending VT100 escape sequences and thereadline.Interface knows nothing about it, these changes only happen on the screen while iface.line and iface.cursor just keep accumulating. When the total count of the characters that you've written via iface.write() becomes equal to or exceeds the count of columns in your terminal, the interface refreshes the line while creating a new blank one and all those characters become visible.

In this case using process.stdout.write() instead of iface.write() is a perfectly valid solution, I don't see anything in this snippet of code that requires readline.Interface functionality. (And yes, you are not obliged to use readline.Interface if you use some functions of the readline module itself; to the contrary, I'd say things like readline.clearLine() and readline.moveCursor() should have better been in the tty module.)

However, if the code snippet in this issue is purely synthetic while you encountered the issue in the situation when using readline.Interface is necessary, I'd suggest you another workaround:

18,19c18
<   readline.clearLine(process.stdout, 0);
<   readline.moveCursor(process.stdout, (-string.length - 4), 0);
---
>   iface.write(null, { ctrl: true, name: 'u' });

This will send the "Ctrl+U" keys to the readline interface, effectively clearing the current line properly. Feel free to turn this issue into a feature-request for a new public method of readline.Interface (there is an undocumented iface.clearLine() already, but it doesn't do exactly what you need).

All 3 comments

Can reproduce with the last Node.js 4, 6, 7, and 8 on Windows 7 x64 with cmd.exe.

The problem is that readline.clearLine() and readline.moveCursor() functions operate on a raw TTY stream sending VT100 escape sequences and thereadline.Interface knows nothing about it, these changes only happen on the screen while iface.line and iface.cursor just keep accumulating. When the total count of the characters that you've written via iface.write() becomes equal to or exceeds the count of columns in your terminal, the interface refreshes the line while creating a new blank one and all those characters become visible.

In this case using process.stdout.write() instead of iface.write() is a perfectly valid solution, I don't see anything in this snippet of code that requires readline.Interface functionality. (And yes, you are not obliged to use readline.Interface if you use some functions of the readline module itself; to the contrary, I'd say things like readline.clearLine() and readline.moveCursor() should have better been in the tty module.)

However, if the code snippet in this issue is purely synthetic while you encountered the issue in the situation when using readline.Interface is necessary, I'd suggest you another workaround:

18,19c18
<   readline.clearLine(process.stdout, 0);
<   readline.moveCursor(process.stdout, (-string.length - 4), 0);
---
>   iface.write(null, { ctrl: true, name: 'u' });

This will send the "Ctrl+U" keys to the readline interface, effectively clearing the current line properly. Feel free to turn this issue into a feature-request for a new public method of readline.Interface (there is an undocumented iface.clearLine() already, but it doesn't do exactly what you need).

This issue has been inactive for sufficiently long that it seems like perhaps it should be closed. Feel free to re-open (or leave a comment requesting that it be re-opened) if you disagree. I'm just tidying up and not acting on a super-strong opinion or anything like that.

Was this page helpful?
0 / 5 - 0 ratings