diff --git a/Cargo.lock b/Cargo.lock index 5a7795d..40bbd09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,38 +89,41 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "crokey" -version = "0.5.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a9d12db57046a9063a1826dba50c7e77d45aed3e661dbfcf48fc7dc9189732" +checksum = "520e83558f4c008ac06fa6a86e5c1d4357be6f994cce7434463ebcdaadf47bb1" dependencies = [ "crokey-proc_macros", "crossterm", "once_cell", "serde", + "strict", ] [[package]] name = "crokey-proc_macros" -version = "0.5.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc92863b630b95cb62f4fc1cef080110cc19a14da0a321fd808a60ddb57c3d3" +checksum = "370956e708a1ce65fe4ac5bb7185791e0ece7485087f17736d54a23a0895049f" dependencies = [ + "crossterm", "proc-macro2", "quote", + "strict", "syn 1.0.109", ] [[package]] name = "crossterm" -version = "0.24.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9f7409c70a38a56216480fba371ee460207dd8926ccf5b4160591759559170" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "crossterm_winapi", - "libc", "mio", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -189,6 +192,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "itoa" version = "1.0.11" @@ -250,14 +259,15 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "log", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -500,6 +510,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "strict" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42444fea5b87a39db4218d9422087e66a85d0e7a0963a439b07bcdf91804006" + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 22e46ae..bf9d0c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,14 +11,14 @@ categories = ["command-line-utilities"] [dependencies] -crossterm = "0.24.0" +crossterm = "0.28" dirs = "5.0" regex = "1.10" serde_json = "1.0" serde = { version = "1.0", features = ["rc"] } textwrap = "0.16" unicode-segmentation = "1.12" -crokey = "0.5" +crokey = "1.1" strum_macros = "0.26" strum = { version = "0.26", features = ["derive"] } diff --git a/src/app_state.rs b/src/app_state.rs index 02ee20c..2dd3b7f 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -603,7 +603,7 @@ impl TereAppState { pub fn move_cursor(&mut self, amount: isize, wrap: bool) { let old_cursor_pos = self.cursor_pos; let n_visible_items = self.visible_items().len(); - let max_cursor_pos = self.main_win_h - 1; + let max_cursor_pos = self.main_win_h.saturating_sub(1); let old_scroll_pos = self.scroll_pos; // pointer_pos: the global location of the cursor in ls_output_buf diff --git a/src/settings.rs b/src/settings.rs index 5c90054..8ad73cb 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -238,7 +238,8 @@ impl TereSettings { if let Some(false) = args.get_one::("clear-default-keymap") { ret.keymap = DEFAULT_KEYMAP .iter() - .map(|(k, c, a)| ((*k, c.clone()), a.clone())) + // (*k).into() converts crokey KeyCombinaton to crossterm KeyEvent + .map(|(k, c, a)| (((*k).into(), c.clone()), a.clone())) .collect(); } @@ -322,7 +323,7 @@ fn parse_keymap_arg(arg: &str) -> Result, )) }; - ret.push((k, c, a)); + ret.push((k.into(), c, a)); } Ok(ret) @@ -330,7 +331,9 @@ fn parse_keymap_arg(arg: &str) -> Result, // NOTE: can't create a const hashmap (without an extra dependency like phf), so just using a slice // of tuples. -pub const DEFAULT_KEYMAP: &[(KeyEvent, ActionContext, Action)] = &[ +// NOTE: The key combinations are saved as crokey KeyCombinations, and converted to crossterm +// KeyEvents when this array is read during initialization +pub const DEFAULT_KEYMAP: &[(crokey::KeyCombination, ActionContext, Action)] = &[ (key!(enter), ActionContext::None, Action::ChangeDir), (key!(right), ActionContext::None, Action::ChangeDir), @@ -416,7 +419,7 @@ mod tests { DEFAULT_KEYMAP .iter() - .for_each(|(k, c, _)| *key_counts.entry((*k, c.clone())).or_default() += 1); + .for_each(|(k, c, _)| *key_counts.entry(((*k).into(), c.clone())).or_default() += 1); for (k, v) in key_counts { assert_eq!(v, 1, "found {} entries for key {:?} in context {:?}", v, k.0, k.1); @@ -443,7 +446,7 @@ mod tests { let m = parse_keymap_arg("ctrl-x:Exit").unwrap(); assert_eq!(m.len(), 1); let (e, c, a) = &m[0]; - assert_eq!(e, &key!(ctrl-x)); + assert_eq!(e, &key!(ctrl-x).into()); assert_eq!(c, &ActionContext::None); assert_eq!(a, &Action::Exit); } @@ -452,10 +455,10 @@ mod tests { fn test_parse_keymap_arg2() { let m = parse_keymap_arg("ctrl-x:Exit,ctrl-j:NotSearching:CursorUp").unwrap(); assert_eq!(m.len(), 2); - assert_eq!(m[0].0, key!(ctrl-x)); + assert_eq!(m[0].0, key!(ctrl-x).into()); assert_eq!(m[0].1, ActionContext::None); assert_eq!(m[0].2, Action::Exit); - assert_eq!(m[1].0, key!(ctrl-j)); + assert_eq!(m[1].0, key!(ctrl-j).into()); assert_eq!(m[1].1, ActionContext::NotSearching); assert_eq!(m[1].2, Action::CursorUp); } @@ -466,7 +469,7 @@ mod tests { "foo", "-m", "ctrl-x:Exit", ]); - assert_eq!(settings.keymap.get(&(key!(ctrl-x), ActionContext::None)), Some(&Action::Exit)); + assert_eq!(settings.keymap.get(&(key!(ctrl-x).into(), ActionContext::None)), Some(&Action::Exit)); } #[test] @@ -475,8 +478,8 @@ mod tests { "foo", "-m", "ctrl-x:Exit,ctrl-y:ClearSearch", ]); - assert_eq!(settings.keymap.get(&(key!(ctrl-x), ActionContext::None)), Some(&Action::Exit)); - assert_eq!(settings.keymap.get(&(key!(ctrl-y), ActionContext::None)), Some(&Action::ClearSearch)); + assert_eq!(settings.keymap.get(&(key!(ctrl-x).into(), ActionContext::None)), Some(&Action::Exit)); + assert_eq!(settings.keymap.get(&(key!(ctrl-y).into(), ActionContext::None)), Some(&Action::ClearSearch)); } #[test] @@ -485,7 +488,7 @@ mod tests { "foo", "-m", "ctrl-x:Exit,ctrl-x:ClearSearch", // repeated mapping ]); - assert_eq!(settings.keymap.get(&(key!(ctrl-x), ActionContext::None)), Some(&Action::ClearSearch)); + assert_eq!(settings.keymap.get(&(key!(ctrl-x).into(), ActionContext::None)), Some(&Action::ClearSearch)); } #[test] @@ -496,7 +499,7 @@ mod tests { "-m", "ctrl-x:Exit", "-m", "ctrl-x:ClearSearch", ]); - assert_eq!(settings.keymap.get(&(key!(ctrl-x), ActionContext::None)), Some(&Action::ClearSearch)); + assert_eq!(settings.keymap.get(&(key!(ctrl-x).into(), ActionContext::None)), Some(&Action::ClearSearch)); } #[test] @@ -564,19 +567,19 @@ mod tests { let settings = parse_cli_no_warnings(vec![ "foo", ]); - assert_eq!(settings.keymap.get(&(key!(alt-h), ActionContext::None)), Some(&Action::ChangeDirParent)); - assert_eq!(settings.keymap.get(&(key!(alt-j), ActionContext::None)), Some(&Action::CursorDown)); - assert_eq!(settings.keymap.get(&(key!(alt-k), ActionContext::None)), Some(&Action::CursorUp)); - assert_eq!(settings.keymap.get(&(key!(alt-l), ActionContext::None)), Some(&Action::ChangeDir)); + assert_eq!(settings.keymap.get(&(key!(alt-h).into(), ActionContext::None)), Some(&Action::ChangeDirParent)); + assert_eq!(settings.keymap.get(&(key!(alt-j).into(), ActionContext::None)), Some(&Action::CursorDown)); + assert_eq!(settings.keymap.get(&(key!(alt-k).into(), ActionContext::None)), Some(&Action::CursorUp)); + assert_eq!(settings.keymap.get(&(key!(alt-l).into(), ActionContext::None)), Some(&Action::ChangeDir)); let settings = parse_cli_no_warnings(vec![ "foo", "-m", "alt-h:None,alt-j:None,alt-k:None,alt-l:None", ]); - assert_eq!(settings.keymap.get(&(key!(alt-h), ActionContext::None)), None); - assert_eq!(settings.keymap.get(&(key!(alt-j), ActionContext::None)), None); - assert_eq!(settings.keymap.get(&(key!(alt-k), ActionContext::None)), None); - assert_eq!(settings.keymap.get(&(key!(alt-l), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(alt-h).into(), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(alt-j).into(), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(alt-k).into(), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(alt-l).into(), ActionContext::None)), None); } #[test] @@ -584,13 +587,13 @@ mod tests { let settings = parse_cli_no_warnings(vec![ "foo", ]); - assert_eq!(settings.keymap.get(&(key!(esc), ActionContext::NotSearching)), Some(&Action::Exit)); - assert_eq!(settings.keymap.get(&(key!(esc), ActionContext::Searching)), Some(&Action::ClearSearch)); - assert_eq!(settings.keymap.get(&(key!(esc), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(esc).into(), ActionContext::NotSearching)), Some(&Action::Exit)); + assert_eq!(settings.keymap.get(&(key!(esc).into(), ActionContext::Searching)), Some(&Action::ClearSearch)); + assert_eq!(settings.keymap.get(&(key!(esc).into(), ActionContext::None)), None); - assert_eq!(settings.keymap.get(&(key!(backspace), ActionContext::Searching)), Some(&Action::EraseSearchChar)); - assert_eq!(settings.keymap.get(&(key!(backspace), ActionContext::NotSearching)), Some(&Action::ChangeDirParent)); - assert_eq!(settings.keymap.get(&(key!(backspace), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(backspace).into(), ActionContext::Searching)), Some(&Action::EraseSearchChar)); + assert_eq!(settings.keymap.get(&(key!(backspace).into(), ActionContext::NotSearching)), Some(&Action::ChangeDirParent)); + assert_eq!(settings.keymap.get(&(key!(backspace).into(), ActionContext::None)), None); let settings = parse_cli_no_warnings(vec![ "foo", @@ -598,9 +601,9 @@ mod tests { "-m", "backspace:None", // this shouldn't affect any of the mappings since they are context-dependent "-m", "backspace:None:None", // this shouldn't affect any of the mappings since they are context-dependent ]); - assert_eq!(settings.keymap.get(&(key!(esc), ActionContext::NotSearching)), Some(&Action::Exit)); - assert_eq!(settings.keymap.get(&(key!(esc), ActionContext::Searching)), None); - assert_eq!(settings.keymap.get(&(key!(esc), ActionContext::None)), None); + assert_eq!(settings.keymap.get(&(key!(esc).into(), ActionContext::NotSearching)), Some(&Action::Exit)); + assert_eq!(settings.keymap.get(&(key!(esc).into(), ActionContext::Searching)), None); + assert_eq!(settings.keymap.get(&(key!(esc).into(), ActionContext::None)), None); } #[test] diff --git a/src/ui/help_window.rs b/src/ui/help_window.rs index 35febd0..a2fa4a1 100644 --- a/src/ui/help_window.rs +++ b/src/ui/help_window.rs @@ -77,7 +77,7 @@ fn get_keyboard_shortcuts_table() -> &'static str { fn get_justified_keyboard_shortcuts_table( key_mapping: &HashMap<(KeyEvent, ActionContext), Action>, ) -> String { - let formatter = crokey::KeyEventFormat::default(); + let formatter = crokey::KeyCombinationFormat::default(); let keyboard_shortcuts = get_keyboard_shortcuts_table(); @@ -156,7 +156,7 @@ fn invert_key_mapping_sorted( // probably not the right place for this, but I'll move // it out if I need it elsewhere. fn cmp_key_events(k1: &KeyEvent, k2: &KeyEvent) -> std::cmp::Ordering { - let formatter = crokey::KeyEventFormat::default(); + let formatter = crokey::KeyCombinationFormat::default(); match (k1.modifiers.is_empty(), k2.modifiers.is_empty()) { (true, true) | (false, false) => { // both or neither have modifiers, sort alphabetically @@ -251,7 +251,7 @@ mod tests { .collect(); for k in key_combos { key_mappings - .entry(k) + .entry(k.into()) .and_modify(|a| a.push(action.clone())) .or_insert_with(|| vec![action.clone()]); } @@ -277,8 +277,8 @@ mod tests { continue; } - let key_combo_str = crokey::KeyEventFormat::default().to_string(*key_combo); - let actions = key_mappings.get(key_combo).unwrap_or_else(|| { + let key_combo_str = crokey::KeyCombinationFormat::default().to_string(*key_combo); + let actions = key_mappings.get(&(*key_combo).into()).unwrap_or_else(|| { panic!( "Key mapping {}:{} not found in README", key_combo_str, expected_action, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 9faaa74..b8aeeb2 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -4,7 +4,7 @@ pub mod markup_render; use std::convert::TryFrom; use std::fmt::Write as _; -use std::io::{Stderr, Write}; +use std::io::{Stderr, Write, Result as IoResult}; use std::path::PathBuf; use crate::app_state::{CdResult, TereAppState}; @@ -31,7 +31,6 @@ use crossterm::{ EnableMouseCapture, DisableMouseCapture, }, - Result as CTResult, }; use dirs::home_dir; @@ -51,13 +50,13 @@ pub struct TereTui<'a> { /// Return the current terminal size as a pair of `(usize, usize)` instead of `(u16, 16)` as /// is done by crossterm. -fn terminal_size_usize() -> CTResult<(usize, usize)> { +fn terminal_size_usize() -> IoResult<(usize, usize)> { let (w, h): (u16, u16) = terminal::size()?; Ok((w as usize, h as usize)) } // Dimensions (width, height) of main window -pub fn main_window_size() -> CTResult<(usize, usize)> { +pub fn main_window_size() -> IoResult<(usize, usize)> { let (w, h) = terminal_size_usize()?; Ok(( w, @@ -86,7 +85,7 @@ impl<'a> TereTui<'a> { /// Queue up a command to clear a given row (starting from 0). Must be executed/flushed /// separately. - fn queue_clear_row(&mut self, row: usize) -> CTResult<()> { + fn queue_clear_row(&mut self, row: usize) -> IoResult<()> { queue!( self.window, cursor::MoveTo(0, u16::try_from(row).unwrap_or(u16::MAX)), @@ -94,7 +93,7 @@ impl<'a> TereTui<'a> { ) } - fn redraw_header(&mut self) -> CTResult<()> { + fn redraw_header(&mut self) -> IoResult<()> { //TODO: what to do if window is narrower than path? // add "..." to beginning? or collapse folder names? make configurable? // at least, truncate towards the left instead of to the right @@ -119,15 +118,15 @@ impl<'a> TereTui<'a> { ) } - fn update_header(&mut self) -> CTResult<()> { + fn update_header(&mut self) -> IoResult<()> { self.app_state.update_header(); // TODO: consider removing redraw here... (is inconsistent with the rest of the 'update' functions) self.redraw_header() } - fn redraw_info_window(&mut self) -> CTResult<()> { + fn redraw_info_window(&mut self) -> IoResult<()> { let (w, h) = terminal_size_usize()?; - let info_win_row = h - FOOTER_SIZE - INFO_WIN_SIZE; + let info_win_row = h.saturating_sub(FOOTER_SIZE + INFO_WIN_SIZE); self.queue_clear_row(info_win_row)?; let mut win = self.window; @@ -146,21 +145,21 @@ impl<'a> TereTui<'a> { } /// Set/update the current info message and redraw the info window - fn info_message(&mut self, msg: &str) -> CTResult<()> { + fn info_message(&mut self, msg: &str) -> IoResult<()> { //TODO: add thread with timeout that will clear the info message after x seconds? self.app_state.info_msg = msg.to_string(); self.redraw_info_window() } - fn error_message(&mut self, msg: &str) -> CTResult<()> { + fn error_message(&mut self, msg: &str) -> IoResult<()> { //TODO: red color (also: make it configurable) let error_msg = format!("error: {}", &msg); self.info_message(&error_msg) } - fn redraw_footer(&mut self) -> CTResult<()> { + fn redraw_footer(&mut self) -> IoResult<()> { let (w, h) = terminal_size_usize()?; - let footer_win_row = h - FOOTER_SIZE; + let footer_win_row = h.saturating_sub(FOOTER_SIZE); self.queue_clear_row(footer_win_row)?; let mut win = self.window; @@ -231,7 +230,7 @@ impl<'a> TereTui<'a> { ) } - fn draw_main_window_row(&mut self, row: usize, highlight: bool) -> CTResult<()> { + fn draw_main_window_row(&mut self, row: usize, highlight: bool) -> IoResult<()> { let row_abs = row + HEADER_SIZE; let width: usize = main_window_size()?.0; @@ -371,17 +370,17 @@ impl<'a> TereTui<'a> { } // redraw row 'row' (relative to the top of the main window) without highlighting - fn unhighlight_row(&mut self, row: usize) -> CTResult<()> { + fn unhighlight_row(&mut self, row: usize) -> IoResult<()> { self.draw_main_window_row(row, false) } - fn highlight_row(&mut self, row: usize) -> CTResult<()> { + fn highlight_row(&mut self, row: usize) -> IoResult<()> { // Highlight the row `row` in the main window. Row 0 is the first row of // the main window self.draw_main_window_row(row, true) } - fn queue_clear_main_window(&mut self) -> CTResult<()> { + fn queue_clear_main_window(&mut self) -> IoResult<()> { let (_, h) = main_window_size()?; for row in HEADER_SIZE..(h + HEADER_SIZE) { self.queue_clear_row(row)?; @@ -389,14 +388,14 @@ impl<'a> TereTui<'a> { Ok(()) } - fn highlight_row_exclusive(&mut self, row: usize) -> CTResult<()> { + fn highlight_row_exclusive(&mut self, row: usize) -> IoResult<()> { // Highlight the row `row` exclusively, and hide all other rows. self.queue_clear_main_window()?; self.highlight_row(row)?; Ok(()) } - fn redraw_main_window(&mut self) -> CTResult<()> { + fn redraw_main_window(&mut self) -> IoResult<()> { let (_, max_y) = main_window_size()?; let mut win = self.window; @@ -417,7 +416,7 @@ impl<'a> TereTui<'a> { win.flush() } - fn redraw_all_windows(&mut self) -> CTResult<()> { + fn redraw_all_windows(&mut self) -> IoResult<()> { self.redraw_header()?; self.redraw_info_window()?; self.redraw_footer()?; @@ -427,7 +426,7 @@ impl<'a> TereTui<'a> { /// Update the app state by moving the cursor by the specified amount, and /// redraw the view as necessary. - fn move_cursor(&mut self, amount: isize, wrap: bool) -> CTResult<()> { + fn move_cursor(&mut self, amount: isize, wrap: bool) -> IoResult<()> { let old_cursor_pos = self.app_state.cursor_pos; let old_scroll_pos = self.app_state.scroll_pos; @@ -448,7 +447,7 @@ impl<'a> TereTui<'a> { /// message to the UI and return false. /// NOTE: path is a string, so that it can be an empty string, which refers to the item under /// the cursor - fn change_dir(&mut self, path: &str) -> CTResult { + fn change_dir(&mut self, path: &str) -> IoResult { //TODO: if there are no visible items, don't do anything? let res = match self.app_state.change_dir(path) { Ok(res) => { @@ -489,7 +488,7 @@ impl<'a> TereTui<'a> { Ok(res) } - fn on_search_char(&mut self, c: char) -> CTResult<()> { + fn on_search_char(&mut self, c: char) -> IoResult<()> { self.app_state.advance_search(&c.to_string()); let n_matches = self.app_state.num_matching_items(); if n_matches == 1 { @@ -510,18 +509,18 @@ impl<'a> TereTui<'a> { self.on_matches_changed() } - fn erase_search_char(&mut self) -> CTResult<()> { + fn erase_search_char(&mut self) -> IoResult<()> { self.app_state.erase_search_char(); self.on_matches_changed() } - fn on_clear_search(&mut self) -> CTResult<()> { + fn on_clear_search(&mut self) -> IoResult<()> { self.app_state.clear_search(); self.on_matches_changed() } /// Things to do when the matches are possibly changed - fn on_matches_changed(&mut self) -> CTResult<()> { + fn on_matches_changed(&mut self) -> IoResult<()> { if self.app_state.is_searching() && self.app_state.num_matching_items() == 0 { self.info_message( self.app_state @@ -536,13 +535,13 @@ impl<'a> TereTui<'a> { Ok(()) } - fn update_main_window_dimensions(&mut self) -> CTResult<()> { + fn update_main_window_dimensions(&mut self) -> IoResult<()> { let (w, h) = main_window_size()?; self.app_state.update_main_window_dimensions(w, h); Ok(()) } - fn on_cursor_up_down(&mut self, up: bool) -> CTResult<()> { + fn on_cursor_up_down(&mut self, up: bool) -> IoResult<()> { let dir = if up { -1 } else { 1 }; if self.app_state.is_searching() { //TODO: handle case where 'is_searching' but there are no matches - move cursor? @@ -555,7 +554,7 @@ impl<'a> TereTui<'a> { } // When scroling up or down by a screenful (i.e. 'page up' or 'page down') - fn on_cursor_up_down_screen(&mut self, up: bool) -> CTResult<()> { + fn on_cursor_up_down_screen(&mut self, up: bool) -> IoResult<()> { if !self.app_state.is_searching() { let (_, h) = main_window_size()?; let delta = ((h - 1) as isize) * if up { -1 } else { 1 }; @@ -566,7 +565,7 @@ impl<'a> TereTui<'a> { } // When moving the cursor to the top or bottom of the listing - fn on_cursor_top_bottom(&mut self, top: bool) -> CTResult<()> { + fn on_cursor_top_bottom(&mut self, top: bool) -> IoResult<()> { let searching = self.app_state.is_searching(); let match_indices = self.app_state.visible_match_indices(); @@ -597,7 +596,7 @@ impl<'a> TereTui<'a> { Ok(()) } - fn on_go_to_home(&mut self) -> CTResult<()> { + fn on_go_to_home(&mut self) -> IoResult<()> { if let Some(path) = home_dir() { if let Some(path) = path.to_str() { self.change_dir(path)?; @@ -606,7 +605,7 @@ impl<'a> TereTui<'a> { Ok(()) } - fn on_go_to_root(&mut self) -> CTResult<()> { + fn on_go_to_root(&mut self) -> IoResult<()> { // note: this is the same as std::path::Component::RootDir // using a temporary buffer to avoid allocating on the heap, because MAIN_SEPARATOR_STR is // not stable yet, see https://github.com/rust-lang/rust/issues/94071 @@ -615,7 +614,7 @@ impl<'a> TereTui<'a> { Ok(()) } - fn handle_mouse_event(&mut self, event: MouseEvent) -> CTResult<()> { + fn handle_mouse_event(&mut self, event: MouseEvent) -> IoResult<()> { if event.row == 0 { //TODO: change to folder by clicking on path component in header return Ok(()); @@ -636,13 +635,13 @@ impl<'a> TereTui<'a> { Ok(()) } - fn toggle_filter_search_mode(&mut self) -> CTResult<()> { + fn toggle_filter_search_mode(&mut self) -> IoResult<()> { self.app_state .set_filter_search(!self.app_state.settings().filter_search); self.on_matches_changed() } - fn cycle_case_sensitive_mode(&mut self) -> CTResult<()> { + fn cycle_case_sensitive_mode(&mut self) -> IoResult<()> { self.app_state .set_case_sensitive(match self.app_state.settings().case_sensitive { CaseSensitiveMode::IgnoreCase => CaseSensitiveMode::CaseSensitive, @@ -652,7 +651,7 @@ impl<'a> TereTui<'a> { self.on_matches_changed() } - fn cycle_gap_search_mode(&mut self) -> CTResult<()> { + fn cycle_gap_search_mode(&mut self) -> IoResult<()> { self.app_state .set_gap_search_mode(match self.app_state.settings().gap_search_mode { GapSearchMode::GapSearchFromStart => GapSearchMode::NormalSearch, @@ -663,7 +662,7 @@ impl<'a> TereTui<'a> { self.on_matches_changed() } - fn cycle_sort_mode(&mut self) -> CTResult<()> { + fn cycle_sort_mode(&mut self) -> IoResult<()> { self.app_state .set_sort_mode(match self.app_state.settings().sort_mode { SortMode::Name => SortMode::Created, @@ -777,6 +776,13 @@ impl<'a> TereTui<'a> { //e => self.info_message(&format!("{:?}", e))?, // for debugging _ => (), }, + + // Don't do anything for these. As of crossterm 0.27, FocusGained / Lost is only + // available on Windows anyway. + Event::FocusGained => (), + Event::FocusLost => (), + Event::Paste(_) => (), + } }; @@ -791,7 +797,7 @@ impl<'a> TereTui<'a> { .map(|_| self.current_path()) } - fn help_view_loop(&mut self) -> CTResult<()> { + fn help_view_loop(&mut self) -> IoResult<()> { self.info_message("Use ↓/↑ or j/k to scroll. Press Esc, 'q', '?' or Ctrl+c to exit help.")?; // We don't need the help view scroll state anywhere else, so not worth it to put in @@ -841,7 +847,7 @@ impl<'a> TereTui<'a> { } } - fn draw_help_view(&mut self, scroll: usize) -> CTResult<()> { + fn draw_help_view(&mut self, scroll: usize) -> IoResult<()> { queue!( self.window, style::SetAttribute(Attribute::Reset), diff --git a/tests/cli.rs b/tests/cli.rs index e92e98d..815aa30 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -95,7 +95,7 @@ fn first_run_prompt_cancel() -> Result<(), RexpectError> { let ptn = Regex::new("It seems like you are running.*for the first time").unwrap(); // check that first run prompt message is there - assert!(ptn.find(&output).is_some()); + assert!(ptn.find(&output).is_some(), "Could not find pattern {:?} in output {:?}", ptn, output); // check that having pressed 'n' prints the expected message assert_eq!(strip_until_alternate_screen_exit(&output), "Cancelled.\r\n"); @@ -125,7 +125,7 @@ fn first_run_prompt_accept() -> Result<(), RexpectError> { let ptn = Regex::new("It seems like you are running.*for the first time").unwrap(); // check that first run prompt message was printed - assert!(ptn.find(&output).is_some()); + assert!(ptn.find(&output).is_some(), "Could not find pattern {:?} in output {:?}", ptn, output); // check that having pressed 'y' and then esc proceeded to the normal operation // and the history file was created