feat(history): one snap per history

This commit is contained in:
OliverGuy 2026-05-07 01:23:04 +02:00
parent 5c3ea6e2ff
commit 3a42011fd3
2 changed files with 48 additions and 24 deletions

View file

@ -1,5 +1,5 @@
use anyhow::Result; use anyhow::Result;
use yazi_macro::{act, render, succ}; use yazi_macro::{render, succ};
use yazi_shared::data::Data; use yazi_shared::data::Data;
use crate::input::{INPUT_HISTORY, Input, InputOp, parser::HistoryOpt}; use crate::input::{INPUT_HISTORY, Input, InputOp, parser::HistoryOpt};
@ -10,14 +10,10 @@ impl Input {
succ!(); succ!();
} }
let new_value = INPUT_HISTORY.lock().unwrap().navigate(opt.offset, &self.snap().value); if !INPUT_HISTORY.lock().unwrap().navigate(opt.offset, &mut self.snaps, self.limit) {
let Some(value) = new_value else { succ!() }; succ!();
}
let snap = self.snap_mut();
snap.value = value;
snap.cursor = snap.count();
act!(r#move, self)?;
succ!(render!()); succ!(render!());
} }
} }

View file

@ -1,17 +1,20 @@
use std::sync::Mutex; use std::{mem, sync::Mutex};
use super::{InputMode, InputSnaps};
pub static INPUT_HISTORY: Mutex<InputHistory> = Mutex::new(InputHistory::new()); pub static INPUT_HISTORY: Mutex<InputHistory> = Mutex::new(InputHistory::new());
#[derive(Debug, Default)] #[derive(Default)]
pub struct InputHistory { pub struct InputHistory {
entries: Vec<String>, entries: Vec<String>,
entry_snaps: Vec<Option<InputSnaps>>,
idx: Option<usize>, idx: Option<usize>,
draft: String, draft: Option<InputSnaps>,
} }
impl InputHistory { impl InputHistory {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { entries: Vec::new(), idx: None, draft: String::new() } Self { entries: Vec::new(), entry_snaps: Vec::new(), idx: None, draft: None }
} }
pub fn push(&mut self, value: String) { pub fn push(&mut self, value: String) {
@ -20,18 +23,19 @@ impl InputHistory {
} }
if self.entries.last().map(String::as_str) != Some(&value) { if self.entries.last().map(String::as_str) != Some(&value) {
self.entries.push(value); self.entries.push(value);
self.entry_snaps.push(None);
} }
self.reset(); self.reset();
} }
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.idx = None; self.idx = None;
self.draft.clear(); self.draft = None;
} }
pub fn navigate(&mut self, step: i64, current: &str) -> Option<String> { pub fn navigate(&mut self, step: i64, snaps: &mut InputSnaps, limit: usize) -> bool {
if self.entries.is_empty() || step == 0 { if self.entries.is_empty() || step == 0 {
return None; return false;
} }
let len = self.entries.len() as i64; let len = self.entries.len() as i64;
@ -39,18 +43,42 @@ impl InputHistory {
let new_pos = (pos + step).clamp(0, len); let new_pos = (pos + step).clamp(0, len);
if new_pos == pos { if new_pos == pos {
return None; return false;
} }
if new_pos == len { let mode = snaps.current().mode;
self.idx = None;
Some(std::mem::take(&mut self.draft)) // Save current snaps into draft or the slot we're leaving
let old = mem::take(snaps);
if let Some(old_idx) = self.idx {
self.entry_snaps[old_idx] = Some(old);
} else { } else {
if self.idx.is_none() { self.draft = Some(old);
self.draft = current.to_owned();
}
self.idx = Some(new_pos as usize);
Some(self.entries[new_pos as usize].clone())
} }
// Load target snaps
*snaps = if new_pos == len {
self.idx = None;
self.draft.take().unwrap_or_default()
} else {
let new_idx = new_pos as usize;
self.idx = Some(new_idx);
if self.entry_snaps[new_idx].is_none() {
let value = self.entries[new_idx].clone();
self.entry_snaps[new_idx] = Some(Self::initial_snaps(value, mode, limit));
}
self.entry_snaps[new_idx].take().unwrap()
};
true
}
fn initial_snaps(value: String, mode: InputMode, limit: usize) -> InputSnaps {
let mut snaps = InputSnaps::new(value, false, limit);
let snap = snaps.current_mut();
snap.mode = mode;
snap.cursor = snap.count().saturating_sub(mode.delta());
snap.resize(limit);
snaps
} }
} }