summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorqkzk <qu3nt1n@gmail.com>2023-10-21 22:07:32 +0200
committerqkzk <qu3nt1n@gmail.com>2023-10-21 22:07:32 +0200
commit1f8bbbe534eda26c68980589ca5b59e767045a5c (patch)
tree1db97870cc60b621916ee00ed08b9e92cd50ea39 /src
parent1ba111f594824bbeb2c7cb8cf759b1d0594bb29e (diff)
history: when moving back select back the file we were at
Diffstat (limited to 'src')
-rw-r--r--src/event_exec.rs27
-rw-r--r--src/history.rs54
-rw-r--r--src/lib.rs2
-rw-r--r--src/status.rs2
-rw-r--r--src/tab.rs40
-rw-r--r--src/term_manager.rs24
-rw-r--r--src/visited.rs45
7 files changed, 118 insertions, 76 deletions
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);
diff --git a/src/lib.rs b/src/lib.rs
index 3e33b70..c56c2ab 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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();
diff --git a/src/tab.rs b/src/tab.rs
index b46f653..1a906a9 100644
--- a/src/tab.rs
+++ b/src/tab.rs
@@ -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);