Skip to content

Commit

Permalink
feat: backward/forward (#230)
Browse files Browse the repository at this point in the history
  • Loading branch information
ndtoan96 authored Oct 1, 2023
1 parent e5340a2 commit e7eb459
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 10 deletions.
2 changes: 1 addition & 1 deletion core/src/external/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,5 @@ pub async fn clipboard_set(s: impl AsRef<std::ffi::OsStr>) -> Result<()> {
let result =
tokio::task::spawn_blocking(move || set_clipboard(formats::Unicode, s.to_string_lossy()));

Ok(result.await?.map_err(|_| anyhow!("failed to set clipboard"))?)
result.await?.map_err(|_| anyhow!("failed to set clipboard"))
}
82 changes: 82 additions & 0 deletions core/src/manager/backstack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
pub struct Backstack<T: Eq> {
cursor: usize,
stack: Vec<T>,
}

impl<T: Eq> Backstack<T> {
pub fn new(item: T) -> Self { Self { cursor: 0, stack: vec![item] } }

pub fn push(&mut self, item: T) {
if self.stack[self.cursor] == item {
return;
}

self.cursor += 1;
if self.cursor == self.stack.len() {
self.stack.push(item);
} else {
self.stack[self.cursor] = item;
self.stack.truncate(self.cursor + 1);
}

// Only keep 30 items before the cursor, the cleanup threshold is 60
if self.stack.len() > 60 {
let start = self.cursor.saturating_sub(30);
self.stack.drain(..start);
self.cursor -= start;
}
}

#[cfg(test)]
#[inline]
pub fn current(&self) -> &T { &self.stack[self.cursor] }

pub fn shift_backward(&mut self) -> Option<&T> {
if self.cursor > 0 {
self.cursor -= 1;
Some(&self.stack[self.cursor])
} else {
None
}
}

pub fn shift_forward(&mut self) -> Option<&T> {
if self.cursor + 1 == self.stack.len() {
None
} else {
self.cursor += 1;
Some(&self.stack[self.cursor])
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_backstack() {
let mut backstack = Backstack::<u32>::new(1);
assert_eq!(backstack.current(), &1);

backstack.push(2);
backstack.push(3);
assert_eq!(backstack.current(), &3);

assert_eq!(backstack.shift_backward(), Some(&2));
assert_eq!(backstack.shift_backward(), Some(&1));
assert_eq!(backstack.shift_backward(), None);
assert_eq!(backstack.shift_backward(), None);
assert_eq!(backstack.current(), &1);
assert_eq!(backstack.shift_forward(), Some(&2));
assert_eq!(backstack.shift_forward(), Some(&3));
assert_eq!(backstack.shift_forward(), None);

backstack.shift_backward();
backstack.push(4);

assert_eq!(backstack.current(), &4);
assert_eq!(backstack.shift_forward(), None);
assert_eq!(backstack.shift_backward(), Some(&2));
}
}
2 changes: 2 additions & 0 deletions core/src/manager/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod backstack;
mod finder;
mod folder;
mod manager;
Expand All @@ -7,6 +8,7 @@ mod tab;
mod tabs;
mod watcher;

pub use backstack::*;
pub use finder::*;
pub use folder::*;
pub use manager::*;
Expand Down
46 changes: 38 additions & 8 deletions core/src/manager/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ use shared::{Debounce, Defer, InputError, Url};
use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};

use super::{Finder, Folder, Mode, Preview, PreviewLock};
use super::{Backstack, Finder, Folder, Mode, Preview, PreviewLock};
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, Step, BLOCKER};

pub struct Tab {
pub(super) mode: Mode,
pub(super) current: Folder,
pub(super) parent: Option<Folder>,

pub(super) history: BTreeMap<Url, Folder>,
pub(super) preview: Preview,
pub(super) backstack: Backstack<Url>,
pub(super) history: BTreeMap<Url, Folder>,
pub(super) preview: Preview,

finder: Option<Finder>,
search: Option<JoinHandle<Result<()>>>,
Expand All @@ -29,9 +30,10 @@ impl From<Url> for Tab {

Self {
mode: Default::default(),
current: Folder::from(url),
current: Folder::from(url.clone()),
parent,

backstack: Backstack::new(url),
history: Default::default(),
preview: Default::default(),

Expand Down Expand Up @@ -87,6 +89,7 @@ impl Tab {
true
}

// TODO: change to sync, and remove `Event::Cd`
pub async fn cd(&mut self, mut target: Url) -> bool {
let Ok(file) = File::from(target.clone()).await else {
return false;
Expand All @@ -98,30 +101,39 @@ impl Tab {
target = target.parent_url().unwrap();
}

// Already in target
if self.current.cwd == target {
if hovered.map(|h| self.current.hover_force(h)) == Some(true) {
emit!(Hover);
}
return false;
}

// Take parent to history
if let Some(rep) = self.parent.take() {
self.history.insert(rep.cwd.clone(), rep);
}

// Current
let rep = self.history_new(&target);
let rep = mem::replace(&mut self.current, rep);
if rep.cwd.is_regular() {
self.history.insert(rep.cwd.clone(), rep);
}

// Parent
if let Some(parent) = target.parent_url() {
self.parent = Some(self.history_new(&parent));
}

// Hover the file
if let Some(h) = hovered {
self.current.hover_force(h);
}

// Backstack
self.backstack.push(target.clone());

emit!(Refresh);
true
}
Expand All @@ -146,17 +158,22 @@ impl Tab {
return false;
}

// Current
let rep = self.history_new(hovered.url());
let rep = mem::replace(&mut self.current, rep);
if rep.cwd.is_regular() {
self.history.insert(rep.cwd.clone(), rep);
}

// Parent
if let Some(rep) = self.parent.take() {
self.history.insert(rep.cwd.clone(), rep);
}
self.parent = Some(self.history_new(&hovered.parent().unwrap()));

// Backstack
self.backstack.push(hovered.url_owned());

emit!(Refresh);
true
}
Expand All @@ -174,28 +191,41 @@ impl Tab {
return false;
};

// Parent
if let Some(rep) = self.parent.take() {
self.history.insert(rep.cwd.clone(), rep);
}
if let Some(parent) = current.parent_url() {
self.parent = Some(self.history_new(&parent));
}

// Current
let rep = self.history_new(&current);
let rep = mem::replace(&mut self.current, rep);
if rep.cwd.is_regular() {
self.history.insert(rep.cwd.clone(), rep);
}

// Backstack
self.backstack.push(current);

emit!(Refresh);
true
}

// TODO
pub fn back(&mut self) -> bool { false }
pub fn back(&mut self) -> bool {
if let Some(url) = self.backstack.shift_backward().cloned() {
futures::executor::block_on(self.cd(url));
}
false
}

// TODO
pub fn forward(&mut self) -> bool { false }
pub fn forward(&mut self) -> bool {
if let Some(url) = self.backstack.shift_forward().cloned() {
futures::executor::block_on(self.cd(url));
}
false
}

pub fn select(&mut self, state: Option<bool>) -> bool {
if let Some(ref hovered) = self.current.hovered {
Expand Down
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT"],"version":"0.2","flagWords":[]}
{"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","backstack"],"flagWords":[],"language":"en"}

0 comments on commit e7eb459

Please sign in to comment.