diff options
author | qkzk <qu3nt1n@gmail.com> | 2023-10-21 22:07:32 +0200 |
---|---|---|
committer | qkzk <qu3nt1n@gmail.com> | 2023-10-21 22:07:32 +0200 |
commit | 1f8bbbe534eda26c68980589ca5b59e767045a5c (patch) | |
tree | 1db97870cc60b621916ee00ed08b9e92cd50ea39 | |
parent | 1ba111f594824bbeb2c7cb8cf759b1d0594bb29e (diff) |
history: when moving back select back the file we were at
-rw-r--r-- | development.md | 1 | ||||
-rw-r--r-- | src/event_exec.rs | 27 | ||||
-rw-r--r-- | src/history.rs | 54 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/status.rs | 2 | ||||
-rw-r--r-- | src/tab.rs | 40 | ||||
-rw-r--r-- | src/term_manager.rs | 24 | ||||
-rw-r--r-- | src/visited.rs | 45 |
8 files changed, 119 insertions, 76 deletions
diff --git a/development.md b/development.md index 8ef539a..1cc1730 100644 --- a/development.md +++ b/development.md @@ -579,6 +579,7 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [x] FIX sending `Event::User(())` events from refresher hangs skim. Use `Event(Key::AltPageUp)` which is now reserved. - [x] allow file selection from args : -f filename selects a file - [x] more args : dual pane, preview second, display full, show hidden +- [x] history: when moving back select back the file we were at - [ ] document filepicking (from my config etc.). - [ ] avoid multiple refreshs if we edit files ourself diff --git a/src/event_exec.rs b/src/event_exec.rs index db48e95..91d1fce 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -479,15 +479,7 @@ impl EventAction { return Ok(()); } let tab = status.selected(); - tab.history.content.pop(); - let last_index = tab.history.len() - 1; - let last = tab.history.content[last_index].clone(); - tab.set_pathcontent(&last)?; - if let Mode::Tree = tab.mode { - tab.make_tree(colors)? - } - - Ok(()) + tab.back(colors) } /// Move to $HOME aka ~. @@ -598,7 +590,7 @@ impl EventAction { pub fn move_left(status: &mut Status, colors: &Colors) -> Result<()> { let tab = status.selected(); match tab.mode { - Mode::Normal => tab.move_to_parent()?, + Mode::Normal => tab.move_to_parent(colors)?, Mode::Tree => tab.tree_select_parent(colors)?, Mode::InputSimple(_) | Mode::InputCompleted(_) => { tab.input.cursor_left(); @@ -744,7 +736,10 @@ impl EventAction { must_refresh = false; LeaveMode::jump(status, colors)? } - Mode::Navigate(Navigate::History) => LeaveMode::history(status, colors)?, + Mode::Navigate(Navigate::History) => { + must_refresh = false; + LeaveMode::history(status, colors)? + } Mode::Navigate(Navigate::Shortcut) => LeaveMode::shortcut(status, colors)?, Mode::Navigate(Navigate::Trash) => LeaveMode::trash(status, colors)?, Mode::Navigate(Navigate::Bulk) => LeaveMode::bulk(status, colors)?, @@ -1217,7 +1212,6 @@ impl LeaveMode { let marks = status.marks.clone(); let tab = status.selected(); if let Some((_, path)) = marks.selected() { - tab.history.push(path); tab.set_pathcontent(path)?; tab.window.reset(tab.path_content.content.len()); tab.input.reset(); @@ -1465,7 +1459,6 @@ impl LeaveMode { let completed = tab.completion.current_proposition(); let path = string_to_path(completed)?; tab.input.reset(); - tab.history.push(&path); tab.set_pathcontent(&path)?; tab.window.reset(tab.path_content.content.len()); status.update_second_pane_for_preview(colors) @@ -1481,7 +1474,6 @@ impl LeaveMode { .selected() .context("exec shortcut: empty shortcuts")? .clone(); - tab.history.push(&path); tab.set_pathcontent(&path)?; tab.refresh_view()?; status.update_second_pane_for_preview(colors) @@ -1491,15 +1483,16 @@ impl LeaveMode { /// It may fail if the user has no permission to visit the path pub fn history(status: &mut Status, colors: &Colors) -> Result<()> { let tab = status.selected(); - tab.input.reset(); - let path = tab + let (path, file) = tab .history .selected() .context("exec history: path unreachable")? .clone(); tab.set_pathcontent(&path)?; tab.history.drop_queue(); - tab.refresh_view()?; + let index = tab.path_content.select_file(&file); + tab.scroll_to(index); + log::info!("leave history {path:?} {file:?} {index}"); status.update_second_pane_for_preview(colors) } diff --git a/src/history.rs b/src/history.rs new file mode 100644 index 0000000..9ebdcc4 --- /dev/null +++ b/src/history.rs @@ -0,0 +1,54 @@ +use std::path::{Path, PathBuf}; + +use crate::impl_selectable_content; + +type DoublePB = (PathBuf, PathBuf); + +/// A stack of visited paths. +/// We save the last folder and the selected file every time a `PatchContent` is updated. +/// We also ensure not to save the same pair multiple times. +#[derive(Default, Clone)] +pub struct History { + pub content: Vec<DoublePB>, + pub index: usize, +} + +impl History { + /// Add a new path and a selected file in the stack, without duplicates, and select the last + /// one. + pub fn push(&mut self, path: &Path, file: &Path) { + let pair = (path.to_owned(), file.to_owned()); + if !self.content.contains(&pair) { + self.content.push(pair); + self.index = self.len() - 1 + } + } + + /// Drop the last visited paths from the stack, after the selected one. + /// Used to go back a few steps in time. + pub fn drop_queue(&mut self) { + if self.is_empty() { + return; + } + let final_length = self.len() - self.index + 1; + self.content.truncate(final_length); + if self.is_empty() { + self.index = 0 + } else { + self.index = self.len() - 1 + } + } + + /// True iff the last element of the stack has the same + /// path as the one given. + /// Doesn't check the associated file. + /// false if the stack is empty. + pub fn is_this_the_last(&self, path: &Path) -> bool { + if self.is_empty() { + return false; + } + self.content[self.len() - 1].0 == path + } +} + +impl_selectable_content!(DoublePB, History); @@ -18,6 +18,7 @@ pub mod filter; pub mod flagged; pub mod git; pub mod help; +pub mod history; pub mod input; pub mod iso; pub mod keybindings; @@ -42,4 +43,3 @@ pub mod term_manager; pub mod trash; pub mod tree; pub mod utils; -pub mod visited; diff --git a/src/status.rs b/src/status.rs index bc69984..a1dd734 100644 --- a/src/status.rs +++ b/src/status.rs @@ -673,7 +673,6 @@ impl Status { }; let tab = self.selected(); let path = std::path::PathBuf::from(mount_point); - tab.history.push(&path); tab.set_pathcontent(&path)?; tab.refresh_view() } @@ -728,7 +727,6 @@ impl Status { pub fn marks_jump_char(&mut self, c: char, colors: &Colors) -> Result<()> { if let Some(path) = self.marks.get(c) { self.selected().set_pathcontent(&path)?; - self.selected().history.push(&path); } self.selected().refresh_view()?; self.selected().reset_mode(); @@ -10,6 +10,7 @@ use crate::config::{Colors, Settings}; use crate::content_window::ContentWindow; use crate::fileinfo::{FileInfo, FileKind, PathContent}; use crate::filter::FilterKind; +use crate::history::History; use crate::input::Input; use crate::mode::Mode; use crate::opener::execute_in_child; @@ -17,7 +18,6 @@ use crate::preview::{Directory, Preview}; use crate::selectable_content::SelectableContent; use crate::shortcut::Shortcut; use crate::utils::{row_to_window_index, set_clipboard}; -use crate::visited::History; /// Holds every thing about the current tab of the application. /// Most of the mutation is done externally. @@ -44,8 +44,6 @@ pub struct Tab { /// Lines of the previewed files. /// Empty if not in preview mode. pub preview: Preview, - /// Visited directories - pub history: History, /// Predefined shortcuts pub shortcut: Shortcut, /// Last searched string @@ -54,6 +52,8 @@ pub struct Tab { pub directory: Directory, /// The filter use before displaying files pub filter: FilterKind, + /// Visited directories + pub history: History, } impl Tab { @@ -82,8 +82,7 @@ impl Tab { let completion = Completion::default(); let must_quit = false; let preview = Preview::Empty; - let mut history = History::default(); - history.push(&path); + let history = History::default(); let mut shortcut = Shortcut::new(&path); shortcut.extend_with_mount_points(mount_points); let searched = None; @@ -99,12 +98,12 @@ impl Tab { completion, must_quit, preview, - history, shortcut, searched, directory, filter, show_hidden, + history, }) } @@ -197,7 +196,6 @@ impl Tab { .context("Empty directory")? .path .clone(); - self.history.push(childpath); self.set_pathcontent(childpath)?; self.window.reset(self.path_content.content.len()); self.input.cursor_start(); @@ -226,7 +224,10 @@ impl Tab { /// Reset the window. /// Add the last path to the history of visited paths. pub fn set_pathcontent(&mut self, path: &path::Path) -> Result<()> { - self.history.push(path); + self.history.push( + &self.path_content.path, + &self.path_content.selected().context("")?.path, + ); self.path_content .change_directory(path, &self.filter, self.show_hidden)?; self.window.reset(self.path_content.content.len()); @@ -310,20 +311,39 @@ impl Tab { } /// Move to the parent of current path - pub fn move_to_parent(&mut self) -> Result<()> { + pub fn move_to_parent(&mut self, colors: &Colors) -> Result<()> { let path = self.path_content.path.clone(); let Some(parent) = path.parent() else { return Ok(()); }; + if self.history.is_this_the_last(parent) { + self.back(colors)?; + return Ok(()); + } self.set_pathcontent(parent) } + pub fn back(&mut self, colors: &Colors) -> Result<()> { + let Some((path, file)) = self.history.content.pop() else { + return Ok(()); + }; + self.set_pathcontent(&path)?; + let index = self.path_content.select_file(&file); + self.scroll_to(index); + self.history.content.pop(); + if let Mode::Tree = self.mode { + self.make_tree(colors)? + } + + Ok(()) + } + /// Select the parent of current node. /// If we were at the root node, move to the parent and make a new tree. pub fn tree_select_parent(&mut self, colors: &Colors) -> Result<()> { self.directory.unselect_children(); if self.directory.tree.position.len() <= 1 { - self.move_to_parent()?; + self.move_to_parent(colors)?; self.make_tree(colors)? } self.directory.select_parent(colors) diff --git a/src/term_manager.rs b/src/term_manager.rs index f9775c2..62b8935 100644 --- a/src/term_manager.rs +++ b/src/term_manager.rs @@ -650,7 +650,7 @@ impl<'a> WinSecondary<'a> { Navigate::CliInfo => self.cli_info(self.status, canvas), Navigate::Compress => self.compress(canvas, &self.status.compression), Navigate::EncryptedDrive => self.encrypt(self.status, self.tab, canvas), - Navigate::History => self.destination(canvas, &self.tab.history), + Navigate::History => self.history(canvas, &self.tab.history), Navigate::Jump => self.destination(canvas, &self.status.flagged), Navigate::Marks(_) => self.marks(self.status, canvas), Navigate::ShellMenu => self.shell_menu(self.status, canvas), @@ -682,6 +682,28 @@ impl<'a> WinSecondary<'a> { Ok(()) } + fn history( + &self, + canvas: &mut dyn Canvas, + selectable: &impl SelectableContent<(PathBuf, PathBuf)>, + ) -> Result<()> { + canvas.print(0, 0, "Go to...")?; + let content = &selectable.content(); + for (row, pair, attr) in enumerated_colored_iter!(content) { + let mut attr = *attr; + if row == selectable.index() { + attr.effect |= Effect::REVERSE; + } + let _ = canvas.print_with_attr( + row + ContentWindow::WINDOW_MARGIN_TOP, + 4, + pair.0.to_str().context("Unreadable filename")?, + attr, + ); + } + Ok(()) + } + fn bulk( &self, canvas: &mut dyn Canvas, diff --git a/src/visited.rs b/src/visited.rs deleted file mode 100644 index a42099b..0000000 --- a/src/visited.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::path::PathBuf; - -use crate::impl_selectable_content; - -/// A Vec of pathbuf of visited files. -/// It's mostly used as a stack but we want to avoid multiple instances of the -/// same path and visit in a certain order. -/// A `BTreeSet` may be used instead. -/// We also need to drop the queue, which isn't easy with a BTreeSet... -#[derive(Default, Clone)] -pub struct History { - /// The visited paths - pub content: Vec<PathBuf>, - /// The currently selected index. By default it's the last one. - pub index: usize, -} - -impl History { - /// Add a new path in the stack, without duplicates, and select the last - /// one. - pub fn push(&mut self, path: &std::path::Path) { - let path = path.to_path_buf(); - if !self.content.contains(&path) { - self.content.push(path); - self.index = self.len() - 1 - } - } - - /// Drop the last visited paths from the stack, after the selected one. - /// Used to go back a few steps in time. - pub fn drop_queue(&mut self) { - if self.is_empty() { - return; - } - let final_length = self.len() - self.index; - self.content.truncate(final_length); - if self.is_empty() { - self.index = 0 - } else { - self.index = self.len() - 1 - } - } -} - -impl_selectable_content!(PathBuf, History); |