Insert line into terminal

Hi everyone,

I'm trying to write a console app with two threads: one thread is background and sometimes spits out some text lines to terminal, second thread is foreground and is used for user interaction.
The situation: user started typing something, half-way there the background thread prints a text to terminal. The text will be appended to the text that user has entered and then new line inserted and user is left with empty line again.
I want the background thread to not disturb user's input, that means that the text from the background thread must be somehow inserted before current line and all the text entered on the current line so far remains intact.
How can I do it?
Another solution is to remember currently entered text, overwrite current line with the text from background thread and print remembered text back on new line. The problem here is that if I print remembered text on a new line user won't be able to delete it using backspace and type something else if they changed their mind.
Any ideas?

I had to solve exactly this problem in one of my projects. You can do it with a combination of the linefeed and log crates - and you might include another logging library depending on your project.

To start, I use the linefeed crate for the terminal prompt. It provides locks around readers and writers, and you can erase anything that the user was in the middle of typing, print what you want, and restore what the user was doing with just a few lines of code (so it's basically what you suggested, already implemented for you)

// CLI is an instance of linefeed::Interface
// I instantiate it using lazy_static,
// because I want to access it from several places.
let mut writer = CLI.lock_writer_erase()?;
writeln!(writer, "{}", message)?;

Great! Now you can print from one thread without mangling what the user is typing. You just have to make sure that other threads never use println!, and instead use the method above.

Well, that sounds tedious...

You can leverage the log crate here. Instead of using println!, just make sure that your other threads use the log crate's macros. Then you can provide a logging implementation that simply uses the code I have above.

In my project, I happen to be using log4rs, which is on the "highly configurable but you'll need to read docs" end of the spectrum. I implement its Append trait, plus a few other things to configure it. But you could also do something lightweight and implement the Log trait yourself: log - Rust

Let me know if any of that wasn't clear!

3 Likes

Thank you Anthony,
linefeed is exactly what I needed.
I tried to google something like this but all I found were different favors of ncursis implementation and color management crates

1 Like