reposense / RepoSense

Contribution analysis tool for Git repositories
https://reposense.org
MIT License
242 stars 154 forks source link

Restricting command line output to one line #705

Open eugenepeh opened 5 years ago

eugenepeh commented 5 years ago

The console output too much messages; making it difficult to differentiate important messages.

Hence, we should restrict the status message from occupying too many lines on the terminal screen, making it self contained in a single line by overwriting itself.

jinyao-lee commented 5 years ago

I don't know if there's a way to overwrite the same line using logging.. AFAIK, there's no way to overwrite to same line using logging

eugenepeh commented 5 years ago

The logging can be reimplemented if such feature does not exist; can even write our own API too if we have to as it also provides us with greater control.

damithc commented 5 years ago

Alternatively, we can try to format the output such that important messages are hard to miss. Overwriting has the downside that we lose the ability to see the sequence of events that happened before.

eugenepeh commented 5 years ago

Alternatively, we can try to format the output such that important messages are hard to miss.

The intention is to make it less important message to take up less space though. E.g. if we use RepoSense to analyze 50+ repos, its going to flood the CLI with bunch of messages, which can be tedious to scroll through all of them for the error messages.

So my suggestion is to have the info messages to always overwrite itself, leaving the error messages right on top of it.

E.g.

[ERROR] ...
[ERROR] ...
[INFO] ... this should always takes up only one line which inform user purely about the progress ...

If the following line is going to be another error message, delete the last [INFO] line, output the new [ERROR] message, then prints the next [INFO] line again.

Overwriting has the downside that we lose the ability to see the sequence of events that happened before.

If the user wants to keep all the messages, we can add in a verbose flag afterward to disable the overwriting. Otherwise, the log file would've contain all the necessary info in chronological order as well, in the event we need to debug the error that has occurred.

bluein-green commented 5 years ago

I came up with something like this:

terminal_v3

Specifically, I did this in CustomLogFormatter#format, that is used by the console handler:

if (record.getLevel().equals(Level.INFO)) {
    builder.append("\r");
    builder.append("\u001b[1000D");
    builder.append("\u001b[K");
    builder.append(dateFormat.format(new Date(record.getMillis()))).append(" - ");
    builder.append(formatMessage(record))
} else {    // Level.WARNING and higher
    builder.append("\r");
    builder.append(dateFormat.format(new Date(record.getMillis()))).append(" - ");
    builder.append("\u001b[37;43m");
    builder.append("[ERROR]");
    builder.append("\u001b[0m");
    builder.append(" ");
    builder.append(formatMessage(record));
    builder.append("\n");
}

However, it is highly dependent on the width of the terminal because of using \r and \u001b[K to clear one line of printed output on the console for Level.INFO messages (and then print a new message in the same line). For smaller widths, I couldn't achieve the desired effect.

Also, there doesn't seem to be a platform-independent way to determine the width of the terminal in Java unless we want to include libraries (https://stackoverflow.com/questions/1286461/can-i-find-the-console-width-with-java). Finding the width would help to determine the number of lines a message occupies in the terminal.

Are there other ways of making Level.INFO messages (that may span multiple lines) overwrite each other?

eugenepeh commented 5 years ago

However, it is highly dependent on the width of the terminal because of using \r and \u001b[K to clear one line of printed output on the console for Level.INFO messages (and then print a new message in the same line). For smaller widths, I couldn't achieve the desired effect.

For the width problem is it that if the newly printed line is shorter than the existing one, non-overwritten chars from previous line will remain?

bluein-green commented 5 years ago

No - if all messages are short enough to fit in 1 line in the terminal, we can use the escape sequence \u001b[K to clear that line before printing a new, shorter message.

The issue is when some messages are long and occupy multiple lines in the terminal due to line wrapping.

Suppose a long message occupies 2 lines on the terminal due to line wrapping. The cursor will be at the 2nd line. Using \r and \u001b[K at the start of the next message results in the 2nd line being cleared, and that new message being printed in that line.

An example: terminal_example

eugenepeh commented 5 years ago

Hmm, maybe in that case, we could probably make our own print class?

Whenever a call is made to the print function, we will record the length of the text that will be printed. With that info, when we're going to overwrite the previous line, we can backspace that amount of time.

bluein-green commented 5 years ago

Yes that would help with recording the number of lines the old message occupied.

To calculate the number of lines (length of message / width of terminal), we will need the width of terminal. However, this is rather difficult to determine unless we include a library (e.g. jline). I'm not sure about the impact of including the library on performance. Would it be advisable to do so?

eugenepeh commented 5 years ago

If we do the backspace way, we wouldn't need to know the width of the terminal anymore, isnt it? At the same time, this will resolve the difference between different platform too.

bluein-green commented 5 years ago

Ah. Using backspace (\b) worked only for single line messages. For multi-line messages, backspaces only moves the cursor to the left margin, and thus the non-last lines will not get overwritten.

I also tried using saving/restoring the cursor position:

  1. Save position
  2. Clear rest of screen (cursor position and below)
  3. Print
  4. Restore position

but it doesn't work when the messages reach the bottom of the terminal and the terminal has to scroll.

eugenepeh commented 5 years ago

Ah. Using backspace (\b) worked only for single line messages. For multi-line messages, backspaces only moves the cursor to the left margin, and thus the non-last lines will not get overwritten.

I guess this is also cause of the problem with your original solution; it does not have any concept of lines of text. In that case, we may have to restrict non-error messages to fit into single lines.