diff options
author | Helmut K. C. Tessarek <tessarek@evermeet.cx> | 2024-04-18 11:16:54 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-18 16:16:54 +0100 |
commit | 176eae02f7f855ef40a4461caca61d530486113f (patch) | |
tree | 14cbe3b9e30d00997036816fbdb5aac2b0e1567c | |
parent | 2fba4aae933c17f0024110dd9ec4a57d748cf867 (diff) |
feat: show preview auto (#1804)
* feat: show preview auto
* refactor: preview_auto
-rw-r--r-- | atuin-client/config.toml | 5 | ||||
-rw-r--r-- | atuin-client/src/settings.rs | 2 | ||||
-rw-r--r-- | atuin/src/command/client/search/interactive.rs | 200 |
3 files changed, 190 insertions, 17 deletions
diff --git a/atuin-client/config.toml b/atuin-client/config.toml index 254586a5..415fd441 100644 --- a/atuin-client/config.toml +++ b/atuin-client/config.toml @@ -73,6 +73,11 @@ ## useful when the command is longer than the terminal width and is cut off # show_preview = false +## enable or disable automatic preview. It shows a preview, if the command is +## longer than the width of the terminal. It respects max_preview_height. +## (default: true) +# show_preview_auto = true + ## what to do when the escape key is pressed when searching ## possible values: return-original, return-query # exit_mode = "return-original" diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index 035af321..daf8fe34 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -358,6 +358,7 @@ pub struct Settings { pub inline_height: u16, pub invert: bool, pub show_preview: bool, + pub show_preview_auto: bool, pub max_preview_height: u16, pub show_help: bool, pub show_tabs: bool, @@ -615,6 +616,7 @@ impl Settings { .set_default("style", "auto")? .set_default("inline_height", 0)? .set_default("show_preview", false)? + .set_default("show_preview_auto", true)? .set_default("max_preview_height", 4)? .set_default("show_help", true)? .set_default("show_tabs", true)? diff --git a/atuin/src/command/client/search/interactive.rs b/atuin/src/command/client/search/interactive.rs index d2a314ef..7a3a834b 100644 --- a/atuin/src/command/client/search/interactive.rs +++ b/atuin/src/command/client/search/interactive.rs @@ -535,23 +535,28 @@ impl State { #[allow(clippy::cast_possible_truncation)] #[allow(clippy::bool_to_int_with_if)] - #[allow(clippy::too_many_lines)] - fn draw( - &mut self, - f: &mut Frame, - results: &[History], - stats: Option<HistoryStats>, + fn calc_preview_height( settings: &Settings, - ) { - let compact = match settings.style { - atuin_client::settings::Style::Auto => f.size().height < 14, - atuin_client::settings::Style::Compact => true, - atuin_client::settings::Style::Full => false, - }; - let invert = settings.invert; - let border_size = if compact { 0 } else { 1 }; - let preview_width = f.size().width - 2; - let preview_height = if settings.show_preview && self.tab_index == 0 { + results: &[History], + selected: usize, + tab_index: usize, + compact: bool, + border_size: u16, + preview_width: u16, + ) -> u16 { + if settings.show_preview_auto && tab_index == 0 && !results.is_empty() { + let length_current_cmd = results[selected].command.len() as u16; + // The '- 19' takes the characters before the command (duration and time) into account + if length_current_cmd > preview_width - 19 { + std::cmp::min( + settings.max_preview_height, + (length_current_cmd + preview_width - 1 - border_size) + / (preview_width - border_size), + ) + border_size * 2 + } else { + 1 + } + } else if settings.show_preview && !settings.show_preview_auto && tab_index == 0 { let longest_command = results .iter() .max_by(|h1, h2| h1.command.len().cmp(&h2.command.len())); @@ -567,11 +572,40 @@ impl State { .sum(), ) }) + border_size * 2 - } else if compact || self.tab_index == 1 { + } else if compact || tab_index == 1 { 0 } else { 1 + } + } + + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::bool_to_int_with_if)] + #[allow(clippy::too_many_lines)] + fn draw( + &mut self, + f: &mut Frame, + results: &[History], + stats: Option<HistoryStats>, + settings: &Settings, + ) { + let compact = match settings.style { + atuin_client::settings::Style::Auto => f.size().height < 14, + atuin_client::settings::Style::Compact => true, + atuin_client::settings::Style::Full => false, }; + let invert = settings.invert; + let border_size = if compact { 0 } else { 1 }; + let preview_width = f.size().width - 2; + let preview_height = Self::calc_preview_height( + settings, + results, + self.results_state.selected(), + self.tab_index, + compact, + border_size, + preview_width, + ); let show_help = settings.show_help && (!compact || f.size().height > 1); let show_tabs = settings.show_tabs; let chunks = Layout::default() @@ -1141,3 +1175,135 @@ fn set_clipboard(s: String) { any(target_os = "windows", target_os = "macos", target_os = "linux") )))] fn set_clipboard(_s: String) {} + +#[cfg(test)] +mod tests { + use atuin_client::history::History; + use atuin_client::settings::Settings; + + use super::State; + + #[test] + fn calc_preview_height_test() { + let settings_preview_auto = Settings { + show_preview_auto: true, + ..Settings::utc() + }; + + let settings_preview_auto_h2 = Settings { + show_preview_auto: true, + max_preview_height: 2, + ..Settings::utc() + }; + + let settings_preview_h4 = Settings { + show_preview_auto: false, + show_preview: true, + max_preview_height: 4, + ..Settings::utc() + }; + + let cmd_60: History = History::capture() + .timestamp(time::OffsetDateTime::now_utc()) + .command("for i in $(seq -w 10); do echo \"item number $i - abcd\"; done") + .cwd("/") + .build() + .into(); + + let cmd_124: History = History::capture() + .timestamp(time::OffsetDateTime::now_utc()) + .command("echo 'Aurea prima sata est aetas, quae vindice nullo, sponte sua, sine lege fidem rectumque colebat. Poena metusque aberant'") + .cwd("/") + .build() + .into(); + + let cmd_200: History = History::capture() + .timestamp(time::OffsetDateTime::now_utc()) + .command("CREATE USER atuin WITH ENCRYPTED PASSWORD 'supersecretpassword'; CREATE DATABASE atuin WITH OWNER = atuin; \\c atuin; REVOKE ALL PRIVILEGES ON SCHEMA public FROM PUBLIC; echo 'All done. 200 characters'") + .cwd("/") + .build() + .into(); + + let results: Vec<History> = vec![cmd_60, cmd_124, cmd_200]; + + // the selected command does not require a preview + let no_preview = State::calc_preview_height( + &settings_preview_auto, + &results, + 0 as usize, + 0 as usize, + false, + 1, + 80, + ); + // the selected command requires 2 lines + let preview_h2 = State::calc_preview_height( + &settings_preview_auto, + &results, + 1 as usize, + 0 as usize, + false, + 1, + 80, + ); + // the selected command requires 3 lines + let preview_h3 = State::calc_preview_height( + &settings_preview_auto, + &results, + 2 as usize, + 0 as usize, + false, + 1, + 80, + ); + // the selected command requires a preview of 1 line (happens when the command is between preview_width-19 and preview_width) + let preview_one_line = State::calc_preview_height( + &settings_preview_auto, + &results, + 0 as usize, + 0 as usize, + false, + 1, + 66, + ); + // the selected command requires 3 lines, but we have a max preview height limit of 2 + let preview_limit_at_2 = State::calc_preview_height( + &settings_preview_auto_h2, + &results, + 2 as usize, + 0 as usize, + false, + 1, + 80, + ); + // the longest command requires 3 lines + let preview_static_h3 = State::calc_preview_height( + &settings_preview_h4, + &results, + 1 as usize, + 0 as usize, + false, + 1, + 80, + ); + // the longest command requires 10 lines, but we have a max preview height limit of 4 + let preview_static_limit_at_4 = State::calc_preview_height( + &settings_preview_h4, + &results, + 1 as usize, + 0 as usize, + false, + 1, + 20, + ); + + assert_eq!(no_preview, 1); + // 1*2 is the space for the border + assert_eq!(preview_h2, 2 + 1 * 2); + assert_eq!(preview_h3, 3 + 1 * 2); + assert_eq!(preview_one_line, 1 + 1 * 2); + assert_eq!(preview_limit_at_2, 2 + 1 * 2); + assert_eq!(preview_static_h3, 3 + 1 * 2); + assert_eq!(preview_static_limit_at_4, 4 + 1 * 2); + } +} |