summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2023-12-26 20:31:43 +0100
committerSebastian Thiel <sebastian.thiel@icloud.com>2023-12-26 20:49:56 +0100
commit90b65d59f5dde888f81c42e3c812670929b1740a (patch)
tree0cabc5213fe69eaab1b5bf72aa989ff34648486e /src
parent46fece5f295a8fb6f90ff969741f79d7c736c140 (diff)
upgrade to latest verison of tui-crates and native crossterm events. (#203)
Diffstat (limited to 'src')
-rw-r--r--src/interactive/app/eventloop.rs56
-rw-r--r--src/interactive/app/tests/journeys_readonly.rs44
-rw-r--r--src/interactive/app/tests/journeys_with_writes.rs12
-rw-r--r--src/interactive/app/tests/utils.rs15
-rw-r--r--src/interactive/widgets/glob.rs5
-rw-r--r--src/interactive/widgets/help.rs16
-rw-r--r--src/interactive/widgets/mark.rs28
-rw-r--r--src/main.rs4
-rw-r--r--src/options.rs2
9 files changed, 109 insertions, 73 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index 00b37e5..c25b350 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -6,7 +6,8 @@ use crate::interactive::{
SortMode,
};
use anyhow::Result;
-use crosstermion::input::{input_channel, Event, Key};
+use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
+use crosstermion::input::{input_channel, Event};
use dua::{
traverse::{EntryData, Traversal},
WalkOptions, WalkResult,
@@ -104,7 +105,7 @@ impl AppState {
where
B: Backend,
{
- use crosstermion::input::Key::*;
+ use crosstermion::crossterm::event::KeyCode::*;
use FocussedPane::*;
{
@@ -115,7 +116,8 @@ impl AppState {
for event in events {
let key = match event {
Event::Key(key) => key,
- Event::Resize(_, _) => Alt('\r'),
+ Event::Resize(_, _) => refresh_key(),
+ _ => continue,
};
self.reset_message();
@@ -123,20 +125,20 @@ impl AppState {
let glob_focussed = self.focussed == Glob;
let mut tree_view = self.tree_view(traversal);
let mut handled = true;
- match key {
+ match key.code {
Esc => {
if let Some(value) = self.handle_quit(&mut tree_view, window) {
return value;
}
}
- Char('\t') => {
+ Tab => {
self.cycle_focus(window);
}
Char('/') if !glob_focussed => {
self.toggle_glob_search(window);
}
Char('?') if !glob_focussed => self.toggle_help_pane(window),
- Ctrl('c') if !glob_focussed => {
+ Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => {
return Ok(ProcessingResult::ExitRequested(WalkResult {
num_errors: tree_view.traversal.io_errors,
}))
@@ -165,14 +167,12 @@ impl AppState {
}
Glob => {
let glob_pane = window.glob_pane.as_mut().expect("glob pane");
- match key {
- Char('\n') => {
- self.search_glob_pattern(&mut tree_view, &glob_pane.input)
- }
+ match key.code {
+ Enter => self.search_glob_pattern(&mut tree_view, &glob_pane.input),
_ => glob_pane.process_events(key),
}
}
- Main => match key {
+ Main => match key.code {
Char('O') => self.open_that(&tree_view),
Char(' ') => self.mark_entry(
CursorMode::KeepPosition,
@@ -180,12 +180,6 @@ impl AppState {
window,
&tree_view,
),
- Char('d') => self.mark_entry(
- CursorMode::Advance,
- MarkEntryMode::Toggle,
- window,
- &tree_view,
- ),
Char('x') => self.mark_entry(
CursorMode::Advance,
MarkEntryMode::MarkForDeletion,
@@ -195,24 +189,34 @@ impl AppState {
Char('a') => {
self.mark_all_entries(MarkEntryMode::Toggle, window, &tree_view)
}
- Char('u') | Char('h') | Backspace | Left => {
- self.exit_node_with_traversal(&tree_view)
- }
- Char('o') | Char('l') | Char('\n') | Right => {
+ Char('o') | Char('l') | Enter | Right => {
self.enter_node_with_traversal(&tree_view)
}
Char('H') | Home => self.change_entry_selection(CursorDirection::ToTop),
Char('G') | End => self.change_entry_selection(CursorDirection::ToBottom),
- Ctrl('u') | PageUp => self.change_entry_selection(CursorDirection::PageUp),
+ PageUp => self.change_entry_selection(CursorDirection::PageUp),
+ Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.change_entry_selection(CursorDirection::PageUp)
+ }
Char('k') | Up => self.change_entry_selection(CursorDirection::Up),
Char('j') | Down => self.change_entry_selection(CursorDirection::Down),
- Ctrl('d') | PageDown => {
+ PageDown => self.change_entry_selection(CursorDirection::PageDown),
+ Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
self.change_entry_selection(CursorDirection::PageDown)
}
Char('s') => self.cycle_sorting(&tree_view),
Char('m') => self.cycle_mtime_sorting(&tree_view),
Char('c') => self.cycle_count_sorting(&tree_view),
Char('g') => display.byte_vis.cycle(),
+ Char('d') => self.mark_entry(
+ CursorMode::Advance,
+ MarkEntryMode::Toggle,
+ window,
+ &tree_view,
+ ),
+ Char('u') | Char('h') | Backspace | Left => {
+ self.exit_node_with_traversal(&tree_view)
+ }
_ => {}
},
};
@@ -353,7 +357,7 @@ impl TerminalApp {
&mut self.traversal,
&mut self.display,
terminal,
- std::iter::once(Event::Key(Key::Alt('\r'))),
+ std::iter::once(Event::Key(refresh_key())),
)
.ok();
}
@@ -482,3 +486,7 @@ pub enum Interaction {
#[allow(dead_code)]
None,
}
+
+fn refresh_key() -> KeyEvent {
+ KeyEvent::new(KeyCode::Char('\r'), KeyModifiers::ALT)
+}
diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs
index 9e06c3f..6781508 100644
--- a/src/interactive/app/tests/journeys_readonly.rs
+++ b/src/interactive/app/tests/journeys_readonly.rs
@@ -1,7 +1,9 @@
use anyhow::Result;
+use crosstermion::crossterm::event::KeyCode;
use pretty_assertions::assert_eq;
use std::ffi::OsString;
+use crate::interactive::app::tests::utils::into_codes;
use crate::interactive::{
app::tests::{
utils::{
@@ -65,28 +67,28 @@ fn simple_user_journey_read_only() -> Result<()> {
// SORTING
{
// when hitting the M key
- app.process_events(&mut terminal, into_keys(b"m".iter()))?;
+ app.process_events(&mut terminal, into_codes("m"))?;
assert_eq!(
app.state.sorting,
SortMode::MTimeDescending,
"it sets the sort mode to descending by mtime"
);
// when hitting the M key again
- app.process_events(&mut terminal, into_keys(b"m".iter()))?;
+ app.process_events(&mut terminal, into_codes("m"))?;
assert_eq!(
app.state.sorting,
SortMode::MTimeAscending,
"it sets the sort mode to ascending by mtime"
);
// when hitting the C key
- app.process_events(&mut terminal, into_keys(b"c".iter()))?;
+ app.process_events(&mut terminal, into_codes("c"))?;
assert_eq!(
app.state.sorting,
SortMode::CountDescending,
"it sets the sort mode to descending by count"
);
// when hitting the C key again
- app.process_events(&mut terminal, into_keys(b"c".iter()))?;
+ app.process_events(&mut terminal, into_codes("c"))?;
assert_eq!(
app.state.sorting,
SortMode::CountAscending,
@@ -98,7 +100,7 @@ fn simple_user_journey_read_only() -> Result<()> {
"it recomputes the cached entries"
);
// when hitting the S key
- app.process_events(&mut terminal, into_keys(b"s".iter()))?;
+ app.process_events(&mut terminal, into_codes("s"))?;
assert_eq!(
app.state.sorting,
SortMode::SizeDescending,
@@ -110,14 +112,14 @@ fn simple_user_journey_read_only() -> Result<()> {
"it recomputes the cached entries"
);
// when hitting the S key again
- app.process_events(&mut terminal, into_keys(b"s".iter()))?;
+ app.process_events(&mut terminal, into_codes("s"))?;
assert_eq!(
app.state.sorting,
SortMode::SizeAscending,
"it sets the sort mode to ascending by size"
);
// hit the S key again to get Descending - the rest depends on it
- app.process_events(&mut terminal, into_keys(b"s".iter()))?;
+ app.process_events(&mut terminal, into_codes("s"))?;
assert_eq!(app.state.sorting, SortMode::SizeDescending,);
assert_eq!(
@@ -130,35 +132,35 @@ fn simple_user_journey_read_only() -> Result<()> {
// Entry-Navigation
{
// when hitting the j key
- app.process_events(&mut terminal, into_keys(b"j".iter()))?;
+ app.process_events(&mut terminal, into_codes("j"))?;
assert_eq!(
node_by_name(&app, fixture_str(long_root)),
node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()),
"it moves the cursor down and selects the next entry based on the current sort mode"
);
// when hitting it while there is nowhere to go
- app.process_events(&mut terminal, into_keys(b"j".iter()))?;
+ app.process_events(&mut terminal, into_codes("j"))?;
assert_eq!(
node_by_name(&app, fixture_str(long_root)),
node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()),
"it stays at the previous position"
);
// when hitting the k key
- app.process_events(&mut terminal, into_keys(b"k".iter()))?;
+ app.process_events(&mut terminal, into_codes("k"))?;
assert_eq!(
node_by_name(&app, fixture_str(short_root)),
node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()),
"it moves the cursor up and selects the next entry based on the current sort mode"
);
// when hitting the k key again
- app.process_events(&mut terminal, into_keys(b"k".iter()))?;
+ app.process_events(&mut terminal, into_codes("k"))?;
assert_eq!(
node_by_name(&app, fixture_str(short_root)),
node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()),
"it stays at the current cursor position as there is nowhere to go"
);
// when hitting the o key with a directory selected
- app.process_events(&mut terminal, into_keys(b"o".iter()))?;
+ app.process_events(&mut terminal, into_codes("o"))?;
{
let new_root_idx = index_by_name(&app, fixture_str(short_root));
assert_eq!(
@@ -173,7 +175,7 @@ fn simple_user_journey_read_only() -> Result<()> {
);
// when hitting the u key while inside a sub-directory
- app.process_events(&mut terminal, into_keys(b"u".iter()))?;
+ app.process_events(&mut terminal, into_codes("u"))?;
{
assert_eq!(
app.traversal.root_index,
@@ -189,7 +191,7 @@ fn simple_user_journey_read_only() -> Result<()> {
}
// when hitting the u key while inside of the root directory
// We are moving the cursor down just to have a non-default selection
- app.process_events(&mut terminal, into_keys(b"ju".iter()))?;
+ app.process_events(&mut terminal, into_codes("ju"))?;
{
assert_eq!(
app.traversal.root_index,
@@ -207,9 +209,9 @@ fn simple_user_journey_read_only() -> Result<()> {
// Deletion
{
// when hitting the 'd' key (also move cursor back to start)
- app.process_events(&mut terminal, into_keys(b"k".iter()))?;
+ app.process_events(&mut terminal, into_codes("k"))?;
let previously_selected_index = *app.state.navigation().selected.as_ref().unwrap();
- app.process_events(&mut terminal, into_keys(b"d".iter()))?;
+ app.process_events(&mut terminal, into_codes("d"))?;
{
assert_eq!(
Some(1),
@@ -231,7 +233,7 @@ fn simple_user_journey_read_only() -> Result<()> {
// when hitting the 'd' key again
{
- app.process_events(&mut terminal, into_keys(b"d".iter()))?;
+ app.process_events(&mut terminal, into_codes("d"))?;
assert_eq!(
Some(2),
@@ -248,7 +250,7 @@ fn simple_user_journey_read_only() -> Result<()> {
// when hitting the 'd' key once again
{
- app.process_events(&mut terminal, into_keys(b"d".iter()))?;
+ app.process_events(&mut terminal, into_codes("d"))?;
assert_eq!(
Some(1),
@@ -265,7 +267,7 @@ fn simple_user_journey_read_only() -> Result<()> {
}
// when hitting the spacebar (after moving up to the first entry)
{
- app.process_events(&mut terminal, into_keys(b"k ".iter()))?;
+ app.process_events(&mut terminal, into_codes("k "))?;
assert_eq!(
None,
@@ -284,7 +286,7 @@ fn simple_user_journey_read_only() -> Result<()> {
// Marking
{
// select something
- app.process_events(&mut terminal, into_keys(b" j ".iter()))?;
+ app.process_events(&mut terminal, into_codes(" j "))?;
assert_eq!(
Some(false),
app.window.mark_pane.as_ref().map(|p| p.has_focus()),
@@ -298,7 +300,7 @@ fn simple_user_journey_read_only() -> Result<()> {
);
// when advancing the selection to the marker pane
- app.process_events(&mut terminal, into_keys(b"\t".iter()))?;
+ app.process_events(&mut terminal, into_keys(Some(KeyCode::Tab)))?;
{
assert_eq!(
Some(true),
diff --git a/src/interactive/app/tests/journeys_with_writes.rs b/src/interactive/app/tests/journeys_with_writes.rs
index d14f8b4..9a941e6 100644
--- a/src/interactive/app/tests/journeys_with_writes.rs
+++ b/src/interactive/app/tests/journeys_with_writes.rs
@@ -1,9 +1,9 @@
use crate::interactive::app::tests::utils::{
- initialized_app_and_terminal_from_paths, into_keys, WritableFixture,
+ initialized_app_and_terminal_from_paths, into_codes, WritableFixture,
};
use anyhow::Result;
+use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crosstermion::input::Event;
-use crosstermion::input::Key;
use pretty_assertions::assert_eq;
#[test]
@@ -13,7 +13,7 @@ fn basic_user_journey_with_deletion() -> Result<()> {
let (mut terminal, mut app) = initialized_app_and_terminal_from_paths(&[fixture.root.clone()])?;
// With a selection of items
- app.process_events(&mut terminal, into_keys(b"doddd".iter()))?;
+ app.process_events(&mut terminal, into_codes("doddd"))?;
assert_eq!(
app.window.mark_pane.as_ref().map(|p| p.marked().len()),
@@ -26,7 +26,11 @@ fn basic_user_journey_with_deletion() -> Result<()> {
// When selecting the marker window and pressing the combination to delete entries
app.process_events(
&mut terminal,
- vec![Event::Key(Key::Char('\t')), Event::Key(Key::Ctrl('r'))].into_iter(),
+ vec![
+ Event::Key(KeyCode::Tab.into()),
+ Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)),
+ ]
+ .into_iter(),
)?;
assert!(
app.window.mark_pane.is_none(),
diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs
index 5c76f17..f6bc354 100644
--- a/src/interactive/app/tests/utils.rs
+++ b/src/interactive/app/tests/utils.rs
@@ -1,4 +1,5 @@
use anyhow::{Context, Error, Result};
+use crosstermion::crossterm::event::KeyCode;
use dua::{
traverse::{EntryData, Tree, TreeIndex},
ByteFormat, TraversalSorting, WalkOptions,
@@ -20,13 +21,15 @@ use tui_react::Terminal;
use crate::interactive::{app::tests::FIXTURE_PATH, Interaction, TerminalApp};
pub fn into_keys<'a>(
- bytes: impl Iterator<Item = &'a u8> + 'a,
+ codes: impl IntoIterator<Item = KeyCode> + 'a,
) -> impl Iterator<Item = crosstermion::input::Event> + 'a {
- bytes.map(|b| {
- crosstermion::input::Event::Key(crosstermion::input::Key::Char(
- std::char::from_u32(*b as u32).unwrap(),
- ))
- })
+ codes
+ .into_iter()
+ .map(|code| crosstermion::input::Event::Key(code.into()))
+}
+
+pub fn into_codes(input: &str) -> impl Iterator<Item = crosstermion::input::Event> + '_ {
+ into_keys(input.chars().map(KeyCode::Char))
}
pub fn node_by_index(app: &TerminalApp, id: TreeIndex) -> &EntryData {
diff --git a/src/interactive/widgets/glob.rs b/src/interactive/widgets/glob.rs
index 0ef95fd..01c8e91 100644
--- a/src/interactive/widgets/glob.rs
+++ b/src/interactive/widgets/glob.rs
@@ -36,9 +36,8 @@ pub struct GlobPane {
impl GlobPane {
pub fn process_events(&mut self, key: Key) {
- use crosstermion::input::Key::*;
-
- match key {
+ use crosstermion::crossterm::event::KeyCode::*;
+ match key.code {
Char(to_insert) => {
self.enter_char(to_insert);
}
diff --git a/src/interactive/widgets/help.rs b/src/interactive/widgets/help.rs
index 0ec8b8c..0edc53e 100644
--- a/src/interactive/widgets/help.rs
+++ b/src/interactive/widgets/help.rs
@@ -1,5 +1,7 @@
use crate::interactive::CursorDirection;
-use crosstermion::{input::Key, input::Key::*};
+pub use crosstermion::crossterm::event::KeyCode::*;
+use crosstermion::crossterm::event::KeyModifiers;
+use crosstermion::input::Key;
use std::{borrow::Borrow, cell::RefCell};
use tui::{
buffer::Buffer,
@@ -34,13 +36,19 @@ fn margin(r: Rect, margin: u16) -> Rect {
impl HelpPane {
pub fn process_events(&mut self, key: Key) {
- match key {
+ match key.code {
Char('H') => self.scroll_help(CursorDirection::ToTop),
Char('G') => self.scroll_help(CursorDirection::ToBottom),
- Ctrl('u') | PageUp => self.scroll_help(CursorDirection::PageUp),
+ Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.scroll_help(CursorDirection::PageUp)
+ }
+ Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.scroll_help(CursorDirection::PageDown)
+ }
+ PageUp => self.scroll_help(CursorDirection::PageUp),
+ PageDown => self.scroll_help(CursorDirection::PageDown),
Char('k') | Up => self.scroll_help(CursorDirection::Up),
Char('j') | Down => self.scroll_help(CursorDirection::Down),
- Ctrl('d') | PageDown => self.scroll_help(CursorDirection::PageDown),
_ => {}
};
}
diff --git a/src/interactive/widgets/mark.rs b/src/interactive/widgets/mark.rs
index 63ed54f..dc0021e 100644
--- a/src/interactive/widgets/mark.rs
+++ b/src/interactive/widgets/mark.rs
@@ -3,7 +3,8 @@ use crate::interactive::{
app::tree_view::TreeView, fit_string_graphemes_with_ellipsis, widgets::entry_color,
CursorDirection,
};
-use crosstermion::{input::Key, input::Key::*};
+use crosstermion::crossterm::event::KeyModifiers;
+use crosstermion::input::Key;
use dua::{traverse::TreeIndex, ByteFormat};
use itertools::Itertools;
use std::{
@@ -114,21 +115,32 @@ impl MarkPane {
self.marked.into_values().map(|v| v.path)
}
pub fn process_events(mut self, key: Key) -> Option<(Self, Option<MarkMode>)> {
+ use crosstermion::crossterm::event::KeyCode::*;
let action = None;
- match key {
- Ctrl('r') => return Some(self.prepare_deletion(MarkMode::Delete)),
+ match key.code {
+ Char('r') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ return Some(self.prepare_deletion(MarkMode::Delete))
+ }
#[cfg(feature = "trash-move")]
- Ctrl('t') => return Some(self.prepare_deletion(MarkMode::Trash)),
- Char('x') | Char('d') | Char(' ') => {
- return self.remove_selected().map(|s| (s, action))
+ Char('t') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ return Some(self.prepare_deletion(MarkMode::Trash))
}
Char('a') => return None,
Char('H') => self.change_selection(CursorDirection::ToTop),
Char('G') => self.change_selection(CursorDirection::ToBottom),
- Ctrl('u') | PageUp => self.change_selection(CursorDirection::PageUp),
+ PageUp => self.change_selection(CursorDirection::PageUp),
+ Char('u') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.change_selection(CursorDirection::PageUp)
+ }
+ Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.change_selection(CursorDirection::PageDown)
+ }
+ PageDown => self.change_selection(CursorDirection::PageDown),
Char('k') | Up => self.change_selection(CursorDirection::Up),
Char('j') | Down => self.change_selection(CursorDirection::Down),
- Ctrl('d') | PageDown => self.change_selection(CursorDirection::PageDown),
+ Char('x') | Char('d') | Char(' ') => {
+ return self.remove_selected().map(|s| (s, action))
+ }
_ => {}
};
Some((self, action))
diff --git a/src/main.rs b/src/main.rs
index a610f6f..47be1f0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,7 +5,7 @@ use dua::TraversalSorting;
use std::{fs, io, io::Write, path::PathBuf, process};
mod crossdev;
-#[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))]
+#[cfg(feature = "tui-crossplatform")]
mod interactive;
mod options;
@@ -31,7 +31,7 @@ fn main() -> Result<()> {
ignore_dirs: opt.ignore_dirs,
};
let res = match opt.command {
- #[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))]
+ #[cfg(feature = "tui-crossplatform")]
Some(Interactive { input }) => {
use crate::interactive::{Interaction, TerminalApp};
use anyhow::{anyhow, Context};
diff --git a/src/options.rs b/src/options.rs
index 6711795..13705b3 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -84,7 +84,7 @@ pub struct Args {
#[derive(Debug, clap::Subcommand)]
pub enum Command {
/// Launch the terminal user interface
- #[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))]
+ #[cfg(feature = "tui-crossplatform")]
#[clap(name = "interactive", visible_alias = "i")]
Interactive {
/// One or more input files or directories. If unset, we will use all entries in the current working directory.