mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-18 14:21:32 +03:00
feat: backward/forward (#230)
This commit is contained in:
parent
e5340a2beb
commit
e7eb459787
2
core/src/external/clipboard.rs
vendored
2
core/src/external/clipboard.rs
vendored
@ -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
core/src/manager/backstack.rs
Normal file
82
core/src/manager/backstack.rs
Normal file
@ -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));
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
mod backstack;
|
||||
mod finder;
|
||||
mod folder;
|
||||
mod manager;
|
||||
@ -7,6 +8,7 @@ mod tab;
|
||||
mod tabs;
|
||||
mod watcher;
|
||||
|
||||
pub use backstack::*;
|
||||
pub use finder::*;
|
||||
pub use folder::*;
|
||||
pub use manager::*;
|
||||
|
@ -6,7 +6,7 @@ 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 {
|
||||
@ -14,8 +14,9 @@ pub struct Tab {
|
||||
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<()>>>,
|
||||
@ -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(),
|
||||
|
||||
@ -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;
|
||||
@ -98,6 +101,7 @@ 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);
|
||||
@ -105,23 +109,31 @@ impl Tab {
|
||||
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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -174,6 +191,7 @@ impl Tab {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Parent
|
||||
if let Some(rep) = self.parent.take() {
|
||||
self.history.insert(rep.cwd.clone(), rep);
|
||||
}
|
||||
@ -181,21 +199,33 @@ impl Tab {
|
||||
self.parent = Some(self.history_new(&parent));
|
||||
}
|
||||
|
||||
// Current
|
||||
let rep = self.history_new(¤t);
|
||||
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 {
|
||||
|
@ -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"}
|
||||
|
Loading…
Reference in New Issue
Block a user