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 =
|
let result =
|
||||||
tokio::task::spawn_blocking(move || set_clipboard(formats::Unicode, s.to_string_lossy()));
|
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 finder;
|
||||||
mod folder;
|
mod folder;
|
||||||
mod manager;
|
mod manager;
|
||||||
@ -7,6 +8,7 @@ mod tab;
|
|||||||
mod tabs;
|
mod tabs;
|
||||||
mod watcher;
|
mod watcher;
|
||||||
|
|
||||||
|
pub use backstack::*;
|
||||||
pub use finder::*;
|
pub use finder::*;
|
||||||
pub use folder::*;
|
pub use folder::*;
|
||||||
pub use manager::*;
|
pub use manager::*;
|
||||||
|
@ -6,7 +6,7 @@ use shared::{Debounce, Defer, InputError, Url};
|
|||||||
use tokio::{pin, task::JoinHandle};
|
use tokio::{pin, task::JoinHandle};
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
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};
|
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, Step, BLOCKER};
|
||||||
|
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
@ -14,6 +14,7 @@ pub struct Tab {
|
|||||||
pub(super) current: Folder,
|
pub(super) current: Folder,
|
||||||
pub(super) parent: Option<Folder>,
|
pub(super) parent: Option<Folder>,
|
||||||
|
|
||||||
|
pub(super) backstack: Backstack<Url>,
|
||||||
pub(super) history: BTreeMap<Url, Folder>,
|
pub(super) history: BTreeMap<Url, Folder>,
|
||||||
pub(super) preview: Preview,
|
pub(super) preview: Preview,
|
||||||
|
|
||||||
@ -29,9 +30,10 @@ impl From<Url> for Tab {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
mode: Default::default(),
|
mode: Default::default(),
|
||||||
current: Folder::from(url),
|
current: Folder::from(url.clone()),
|
||||||
parent,
|
parent,
|
||||||
|
|
||||||
|
backstack: Backstack::new(url),
|
||||||
history: Default::default(),
|
history: Default::default(),
|
||||||
preview: Default::default(),
|
preview: Default::default(),
|
||||||
|
|
||||||
@ -87,6 +89,7 @@ impl Tab {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: change to sync, and remove `Event::Cd`
|
||||||
pub async fn cd(&mut self, mut target: Url) -> bool {
|
pub async fn cd(&mut self, mut target: Url) -> bool {
|
||||||
let Ok(file) = File::from(target.clone()).await else {
|
let Ok(file) = File::from(target.clone()).await else {
|
||||||
return false;
|
return false;
|
||||||
@ -98,6 +101,7 @@ impl Tab {
|
|||||||
target = target.parent_url().unwrap();
|
target = target.parent_url().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Already in target
|
||||||
if self.current.cwd == target {
|
if self.current.cwd == target {
|
||||||
if hovered.map(|h| self.current.hover_force(h)) == Some(true) {
|
if hovered.map(|h| self.current.hover_force(h)) == Some(true) {
|
||||||
emit!(Hover);
|
emit!(Hover);
|
||||||
@ -105,23 +109,31 @@ impl Tab {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Take parent to history
|
||||||
if let Some(rep) = self.parent.take() {
|
if let Some(rep) = self.parent.take() {
|
||||||
self.history.insert(rep.cwd.clone(), rep);
|
self.history.insert(rep.cwd.clone(), rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Current
|
||||||
let rep = self.history_new(&target);
|
let rep = self.history_new(&target);
|
||||||
let rep = mem::replace(&mut self.current, rep);
|
let rep = mem::replace(&mut self.current, rep);
|
||||||
if rep.cwd.is_regular() {
|
if rep.cwd.is_regular() {
|
||||||
self.history.insert(rep.cwd.clone(), rep);
|
self.history.insert(rep.cwd.clone(), rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent
|
||||||
if let Some(parent) = target.parent_url() {
|
if let Some(parent) = target.parent_url() {
|
||||||
self.parent = Some(self.history_new(&parent));
|
self.parent = Some(self.history_new(&parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hover the file
|
||||||
if let Some(h) = hovered {
|
if let Some(h) = hovered {
|
||||||
self.current.hover_force(h);
|
self.current.hover_force(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backstack
|
||||||
|
self.backstack.push(target.clone());
|
||||||
|
|
||||||
emit!(Refresh);
|
emit!(Refresh);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -146,17 +158,22 @@ impl Tab {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Current
|
||||||
let rep = self.history_new(hovered.url());
|
let rep = self.history_new(hovered.url());
|
||||||
let rep = mem::replace(&mut self.current, rep);
|
let rep = mem::replace(&mut self.current, rep);
|
||||||
if rep.cwd.is_regular() {
|
if rep.cwd.is_regular() {
|
||||||
self.history.insert(rep.cwd.clone(), rep);
|
self.history.insert(rep.cwd.clone(), rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent
|
||||||
if let Some(rep) = self.parent.take() {
|
if let Some(rep) = self.parent.take() {
|
||||||
self.history.insert(rep.cwd.clone(), rep);
|
self.history.insert(rep.cwd.clone(), rep);
|
||||||
}
|
}
|
||||||
self.parent = Some(self.history_new(&hovered.parent().unwrap()));
|
self.parent = Some(self.history_new(&hovered.parent().unwrap()));
|
||||||
|
|
||||||
|
// Backstack
|
||||||
|
self.backstack.push(hovered.url_owned());
|
||||||
|
|
||||||
emit!(Refresh);
|
emit!(Refresh);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -174,6 +191,7 @@ impl Tab {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Parent
|
||||||
if let Some(rep) = self.parent.take() {
|
if let Some(rep) = self.parent.take() {
|
||||||
self.history.insert(rep.cwd.clone(), rep);
|
self.history.insert(rep.cwd.clone(), rep);
|
||||||
}
|
}
|
||||||
@ -181,21 +199,33 @@ impl Tab {
|
|||||||
self.parent = Some(self.history_new(&parent));
|
self.parent = Some(self.history_new(&parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Current
|
||||||
let rep = self.history_new(¤t);
|
let rep = self.history_new(¤t);
|
||||||
let rep = mem::replace(&mut self.current, rep);
|
let rep = mem::replace(&mut self.current, rep);
|
||||||
if rep.cwd.is_regular() {
|
if rep.cwd.is_regular() {
|
||||||
self.history.insert(rep.cwd.clone(), rep);
|
self.history.insert(rep.cwd.clone(), rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backstack
|
||||||
|
self.backstack.push(current);
|
||||||
|
|
||||||
emit!(Refresh);
|
emit!(Refresh);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
pub fn back(&mut self) -> bool {
|
||||||
pub fn back(&mut self) -> bool { false }
|
if let Some(url) = self.backstack.shift_backward().cloned() {
|
||||||
|
futures::executor::block_on(self.cd(url));
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
pub fn forward(&mut self) -> bool {
|
||||||
pub fn forward(&mut self) -> bool { false }
|
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 {
|
pub fn select(&mut self, state: Option<bool>) -> bool {
|
||||||
if let Some(ref hovered) = self.current.hovered {
|
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