console-rs / dialoguer

Rust utility library for nice command line prompts and similar things
MIT License
1.33k stars 142 forks source link

Handling Interrupt Signals (e.g., Ctrl+C) in `Select` and Other Dialoguer Actions #294

Open silentEAG opened 11 months ago

silentEAG commented 11 months ago

using the latest version

dialoguer = { version = "0.11" }

using example-select, when everything operates as expected, it looks like this:

image

however, when I press Ctrl+C in Select, the following occurs:

image

This issue is not limited to the Select; it affects all actions that initially hide the cursor. The root of the problem seems to be the lack of a handler for keys like Ctrl+C.

Is it possible to provide a custom handler to manage these kinds of inputs? Any assistance or suggestions would be greatly appreciated.

jht5945 commented 10 months ago

I found a magic solution:

  1. add crate ctrlc, just cargo add ctrlc
  2. call ctrlc::set_handler before call dialoguer
    let _ = ctrlc::set_handler(move || {
    // DO NOTHING
    });
  3. process dialoguer error
    use dialoguer::console::Term;
    if dialoguer_result.is_err() {
    let _ = Term::stderr().show_cursor();
    }
mike-lloyd03 commented 10 months ago

Thanks for this. It is a much better solution for me with a clap program than showing the cursor in set_handler. My example:

fn main() -> anyhow::Result<()> {
    // Ignore SIGINT so we can handle it ourselves
    ctrlc::set_handler(move || {}).expect("Error setting Ctrl-C handler");

    let app = App::parse();

    match &app.command {
        // Handle clap commands
    }
    .map_err(|e| {
        let _ = Term::stdout().show_cursor();
        e
    })
}
mitsuhiko commented 10 months ago

There are two challenges with dialoguer at the moment about this. The underlying console library is now quite decent at restoring terminal state in more cases, even in light of ctrl-c. However in dialoguer the cursor is explicitly hidden in many interactions which results in this sort of behavior.

It would be weird for console to try to install a ctrl-c handler though. One option would be to say the components do not hide the cursor but Term::read_key_no_cursor is added which is like read_key but does not show the cursor. This would result in similar behavior as we have today but it could safely restore the cursor in light of ctrl-c.

mitsuhiko commented 10 months ago

Playing with this a bit now I do wonder if it would not be a better solution to just provide a convenient way to reset the terminal on ctrl-c with a utility function.

fn main() -> anyhow::Result<()> {
    ctrlc::set_handler(|| console::reset_terminal(); };
    // ...
}