mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: backward/forward
This commit is contained in:
parent
b2d3e39e96
commit
38299ab5be
7 changed files with 107 additions and 19 deletions
|
|
@ -119,9 +119,9 @@ impl App {
|
|||
let manager = &mut self.cx.manager;
|
||||
let tasks = &mut self.cx.tasks;
|
||||
match event {
|
||||
Event::Cd(url) => {
|
||||
Event::Cd(url, backstack_push) => {
|
||||
futures::executor::block_on(async {
|
||||
manager.active_mut().cd(expand_url(url)).await;
|
||||
manager.active_mut().cd(expand_url(url), backstack_push).await;
|
||||
});
|
||||
}
|
||||
Event::Refresh => {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ impl Executor {
|
|||
if exec.named.contains_key("interactive") {
|
||||
cx.manager.active_mut().cd_interactive(url)
|
||||
} else {
|
||||
emit!(Cd(url));
|
||||
emit!(Cd(url, true));
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub enum Event {
|
|||
Call(Vec<Exec>, KeymapLayer),
|
||||
|
||||
// Manager
|
||||
Cd(Url),
|
||||
Cd(Url, bool),
|
||||
Refresh,
|
||||
Files(FilesOp),
|
||||
Pages(usize),
|
||||
|
|
@ -71,8 +71,8 @@ macro_rules! emit {
|
|||
$crate::Event::Call($exec, $layer).emit();
|
||||
};
|
||||
|
||||
(Cd($url:expr)) => {
|
||||
$crate::Event::Cd($url).emit();
|
||||
(Cd($url:expr, $backstack_push:expr)) => {
|
||||
$crate::Event::Cd($url, $backstack_push).emit();
|
||||
};
|
||||
(Files($op:expr)) => {
|
||||
$crate::Event::Files($op).emit();
|
||||
|
|
|
|||
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"))
|
||||
}
|
||||
|
|
|
|||
69
core/src/manager/backstack.rs
Normal file
69
core/src/manager/backstack.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
pub struct BackStack<T> {
|
||||
current: usize,
|
||||
stack: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> BackStack<T> {
|
||||
pub fn new(item: T) -> Self { Self { current: 0, stack: vec![item] } }
|
||||
|
||||
pub fn shift_backward(&mut self) -> Option<&T> {
|
||||
if self.current > 0 {
|
||||
self.current -= 1;
|
||||
Some(&self.stack[self.current])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift_forward(&mut self) -> Option<&T> {
|
||||
if self.current + 1 == self.stack.len() {
|
||||
None
|
||||
} else {
|
||||
self.current += 1;
|
||||
Some(&self.stack[self.current])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, item: T) {
|
||||
self.current += 1;
|
||||
if self.current == self.stack.len() {
|
||||
self.stack.push(item);
|
||||
} else {
|
||||
self.stack[self.current] = item;
|
||||
self.stack.truncate(self.current + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn get_current<T>(backstack: &BackStack<T>) -> &T { &backstack.stack[backstack.current] }
|
||||
|
||||
#[test]
|
||||
fn test_backstack() {
|
||||
let mut backstack = BackStack::<i32>::new(1);
|
||||
assert_eq!(get_current(&backstack), &1);
|
||||
|
||||
backstack.push(2);
|
||||
backstack.push(3);
|
||||
assert_eq!(get_current(&backstack), &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!(get_current(&backstack), &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!(get_current(&backstack), &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;
|
||||
|
|
|
|||
|
|
@ -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::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,6 +14,7 @@ pub struct Tab {
|
|||
pub(super) current: Folder,
|
||||
pub(super) parent: Option<Folder>,
|
||||
|
||||
pub(super) backstack: BackStack<Url>,
|
||||
pub(super) history: BTreeMap<Url, Folder>,
|
||||
pub(super) preview: Preview,
|
||||
|
||||
|
|
@ -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,7 +89,7 @@ impl Tab {
|
|||
true
|
||||
}
|
||||
|
||||
pub async fn cd(&mut self, mut target: Url) -> bool {
|
||||
pub async fn cd(&mut self, mut target: Url, backstack_push: bool) -> bool {
|
||||
let Ok(file) = File::from(target.clone()).await else {
|
||||
return false;
|
||||
};
|
||||
|
|
@ -109,6 +111,10 @@ impl Tab {
|
|||
self.history.insert(rep.cwd.clone(), rep);
|
||||
}
|
||||
|
||||
if backstack_push {
|
||||
self.backstack.push(target.clone());
|
||||
}
|
||||
|
||||
let rep = self.history_new(&target);
|
||||
let rep = mem::replace(&mut self.current, rep);
|
||||
if rep.cwd.is_regular() {
|
||||
|
|
@ -132,7 +138,7 @@ impl Tab {
|
|||
emit!(Input(InputOpt::top("Change directory:").with_value(target.to_string_lossy())));
|
||||
|
||||
if let Some(Ok(s)) = result.recv().await {
|
||||
emit!(Cd(Url::from(s)));
|
||||
emit!(Cd(Url::from(s), true));
|
||||
}
|
||||
});
|
||||
false
|
||||
|
|
@ -157,6 +163,8 @@ impl Tab {
|
|||
}
|
||||
self.parent = Some(self.history_new(&hovered.parent().unwrap()));
|
||||
|
||||
self.backstack.push(self.current.cwd.clone());
|
||||
|
||||
emit!(Refresh);
|
||||
true
|
||||
}
|
||||
|
|
@ -187,15 +195,25 @@ impl Tab {
|
|||
self.history.insert(rep.cwd.clone(), rep);
|
||||
}
|
||||
|
||||
self.backstack.push(self.current.cwd.clone());
|
||||
|
||||
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() {
|
||||
emit!(Cd(url.clone(), false));
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// TODO
|
||||
pub fn forward(&mut self) -> bool { false }
|
||||
pub fn forward(&mut self) -> bool {
|
||||
if let Some(url) = self.backstack.shift_forward() {
|
||||
emit!(Cd(url.clone(), false));
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn select(&mut self, state: Option<bool>) -> bool {
|
||||
if let Some(ref hovered) = self.current.hovered {
|
||||
|
|
@ -309,7 +327,7 @@ impl Tab {
|
|||
let mut first = true;
|
||||
while let Some(chunk) = rx.next().await {
|
||||
if first {
|
||||
emit!(Cd(cwd.clone()));
|
||||
emit!(Cd(cwd.clone(), true));
|
||||
first = false;
|
||||
}
|
||||
emit!(Files(FilesOp::Part(cwd.clone(), ticket, chunk)));
|
||||
|
|
@ -345,7 +363,7 @@ impl Tab {
|
|||
if global { external::fzf(FzfOpt { cwd }) } else { external::zoxide(ZoxideOpt { cwd }) }?;
|
||||
|
||||
if let Ok(target) = rx.await? {
|
||||
emit!(Cd(target));
|
||||
emit!(Cd(target, true));
|
||||
}
|
||||
Ok::<(), Error>(())
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue