diff --git a/wallet/wallet-cli-lib/src/console.rs b/wallet/wallet-cli-lib/src/console.rs index 91ded71952..9e7d4b92bc 100644 --- a/wallet/wallet-cli-lib/src/console.rs +++ b/wallet/wallet-cli-lib/src/console.rs @@ -16,6 +16,7 @@ use std::{collections::VecDeque, path::PathBuf}; use crossterm::tty::IsTty; + use wallet_cli_commands::WalletCliCommandError; use wallet_rpc_lib::types::NodeInterface; diff --git a/wallet/wallet-cli-lib/src/repl/interactive/log.rs b/wallet/wallet-cli-lib/src/repl/interactive/log.rs index 525f3bbf25..7f5e6eb9e2 100644 --- a/wallet/wallet-cli-lib/src/repl/interactive/log.rs +++ b/wallet/wallet-cli-lib/src/repl/interactive/log.rs @@ -67,7 +67,10 @@ impl InteractiveLogger { // Increase the buffer to prevent dropped log output. // Do not use very large buffers here, as this will increase // the amount of allocated memory (even if the buffer is not used). - let external_printer = reedline::ExternalPrinter::new(1024); + // Note that it shouldn't be too small either, because InteractiveLogger is also used + // to collect logs when displaying paginated output; 8k should be enough to collect + // relatively verbose debug logs for several minutes. + let external_printer = reedline::ExternalPrinter::new(8192); let print_directly = Arc::new(Mutex::new(true)); diff --git a/wallet/wallet-cli-lib/src/repl/interactive/mod.rs b/wallet/wallet-cli-lib/src/repl/interactive/mod.rs index 681995bbb3..32a1ba5e27 100644 --- a/wallet/wallet-cli-lib/src/repl/interactive/mod.rs +++ b/wallet/wallet-cli-lib/src/repl/interactive/mod.rs @@ -159,7 +159,7 @@ pub fn run( &mut console, &mut prompt, &mut line_editor, - logger.printer(), + &logger, vi_mode, true, ) { @@ -192,7 +192,7 @@ pub fn run( &mut console, &mut prompt, &mut line_editor, - logger.printer(), + &logger, vi_mode, exit_on_error, ) { @@ -216,7 +216,7 @@ fn handle_response( console: &mut impl ConsoleOutput, prompt: &mut wallet_prompt::WalletPrompt, line_editor: &mut Reedline, - printer: &reedline::ExternalPrinter, + logger: &log::InteractiveLogger, vi_mode: bool, exit_on_error: bool, ) -> CommandResponse { @@ -225,7 +225,8 @@ fn handle_response( console.print_line(&text); } Ok(Some(ConsoleCommand::PaginatedPrint { header, body })) => { - paginate_output(header, body, line_editor, console).expect("Should not fail normally"); + paginate_output(header, body, line_editor, console, logger) + .expect("Should not fail normally"); } Ok(Some(ConsoleCommand::SetStatus { status, @@ -246,7 +247,8 @@ fn handle_response( Ok(Some(ConsoleCommand::Exit)) => return CommandResponse::Exit, Ok(Some(ConsoleCommand::ChoiceMenu(choice))) => { - let line_editor_helper = create_line_editor(printer.clone(), vec![], None, vi_mode); + let line_editor_helper = + create_line_editor(logger.printer().clone(), vec![], None, vi_mode); let cmd = handle_choice_menu(choice.as_ref(), line_editor_helper); if let Some(cmd) = cmd { return CommandResponse::Command(Box::new(cmd)); @@ -317,9 +319,31 @@ fn paginate_output( body: String, line_editor: &mut Reedline, console: &mut impl ConsoleOutput, + logger: &log::InteractiveLogger, ) -> std::io::Result<()> { let mut current_index = 0; + // Disable the direct logging, because: + // a) we don't want any log output to appear in the paginated output; + // b) the log output will appear broken in the raw mode anyway (which we switch the terminal + // into inside `read_command`). + // Note: the logs will be collected inside `reedline::ExternalPrinter` and printed + // by `Readline::read_line` later. + logger.set_print_directly(false); + // Enter the alternate screen, to preserve the existing contents of the terminal. + // Note: this call will basically send a certain ANSI code to the specified stream and then + // flush the stream. It shouldn't matter whether we use stdout or stderr here. + crossterm::execute!(std::io::stdout(), crossterm::terminal::EnterAlternateScreen)?; + // Undo all of the above on function exit. + let _cleanup = OnceDestructor::new(|| { + // Note: we can't do anything about the possible error here. Panicking is probably + // better than ignoring it, because the UI will likely be broken anyway + // (though it's not clear under which conditions such an error can occur). + crossterm::execute!(std::io::stdout(), crossterm::terminal::LeaveAlternateScreen) + .expect("failure when leaving alternate terminal screen"); + logger.set_print_directly(true); + }); + let (mut page_rows, mut offsets) = compute_page_line_offsets(&body); let mut last_batch = offsets.len() - 1; @@ -398,7 +422,8 @@ fn read_command( // TODO: maybe enable raw mode only once per pagination crossterm::terminal::enable_raw_mode()?; let _cleanup = OnceDestructor::new(|| { - crossterm::terminal::disable_raw_mode().expect("Should not fail normally") + // Same as in `paginate_output`, we can't do much about the possible error, so we just panic. + crossterm::terminal::disable_raw_mode().expect("failure when disabling raw terminal mode") }); loop {