mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
refactor: new yazi-widgets crate
This commit is contained in:
parent
7632163678
commit
3fb8c34185
40 changed files with 255 additions and 181 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -3433,6 +3433,7 @@ dependencies = [
|
|||
"yazi-proxy",
|
||||
"yazi-scheduler",
|
||||
"yazi-shared",
|
||||
"yazi-widgets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3501,6 +3502,7 @@ dependencies = [
|
|||
"yazi-plugin",
|
||||
"yazi-proxy",
|
||||
"yazi-shared",
|
||||
"yazi-widgets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3630,6 +3632,21 @@ dependencies = [
|
|||
"yazi-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yazi-widgets"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"tokio",
|
||||
"unicode-width 0.2.0",
|
||||
"yazi-codegen",
|
||||
"yazi-config",
|
||||
"yazi-macro",
|
||||
"yazi-plugin",
|
||||
"yazi-proxy",
|
||||
"yazi-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.5"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" }
|
|||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
yazi-widgets = { path = "../yazi-widgets", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::mem;
|
||||
|
||||
use yazi_macro::render;
|
||||
use yazi_proxy::InputProxy;
|
||||
use yazi_shared::event::CmdCow;
|
||||
|
|
@ -23,7 +25,6 @@ impl Cmp {
|
|||
}
|
||||
|
||||
self.caches.clear();
|
||||
self.visible = false;
|
||||
render!();
|
||||
render!(mem::replace(&mut self.visible, false));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,17 +18,15 @@ impl From<bool> for Opt {
|
|||
impl Input {
|
||||
#[yazi_codegen::command]
|
||||
pub fn close(&mut self, opt: Opt) {
|
||||
if self.completion {
|
||||
CmpProxy::close();
|
||||
}
|
||||
self.visible = false;
|
||||
self.ticket.next();
|
||||
|
||||
if let Some(cb) = self.callback.take() {
|
||||
let value = self.snap_mut().value.clone();
|
||||
if let Some(cb) = self.tx.take() {
|
||||
let value = self.snap().value.clone();
|
||||
_ = cb.send(if opt.submit { Ok(value) } else { Err(InputError::Canceled(value)) });
|
||||
}
|
||||
|
||||
self.ticket = self.ticket.wrapping_add(1);
|
||||
self.visible = false;
|
||||
CmpProxy::close();
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use yazi_macro::render;
|
||||
use yazi_proxy::CmpProxy;
|
||||
use yazi_shared::event::CmdCow;
|
||||
use yazi_widgets::input::InputOp;
|
||||
|
||||
use crate::input::{Input, InputMode, op::InputOp};
|
||||
use crate::input::Input;
|
||||
|
||||
struct Opt;
|
||||
|
||||
|
|
@ -16,28 +17,16 @@ impl From<()> for Opt {
|
|||
impl Input {
|
||||
#[yazi_codegen::command]
|
||||
pub fn escape(&mut self, _: Opt) {
|
||||
let snap = self.snap_mut();
|
||||
match snap.mode {
|
||||
InputMode::Normal if snap.op == InputOp::None => {
|
||||
self.close(false);
|
||||
}
|
||||
InputMode::Normal => {
|
||||
snap.op = InputOp::None;
|
||||
}
|
||||
InputMode::Insert => {
|
||||
snap.mode = InputMode::Normal;
|
||||
self.move_(-1);
|
||||
use yazi_widgets::input::InputMode as M;
|
||||
|
||||
if self.completion {
|
||||
CmpProxy::close();
|
||||
}
|
||||
}
|
||||
InputMode::Replace => {
|
||||
snap.mode = InputMode::Normal;
|
||||
}
|
||||
let mode = self.snap().mode;
|
||||
match mode {
|
||||
M::Normal if self.snap_mut().op == InputOp::None => self.close(false),
|
||||
M::Insert => CmpProxy::close(),
|
||||
M::Normal | M::Replace => {}
|
||||
}
|
||||
|
||||
self.snaps.tag(self.limit());
|
||||
self.inner.escape(());
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(backspace backward close complete delete escape forward insert kill move_ paste redo replace show type_ undo visual yank);
|
||||
yazi_macro::mod_flat!(close escape show);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ impl TryFrom<CmdCow> for Opt {
|
|||
|
||||
impl Input {
|
||||
pub fn show(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else { return };
|
||||
let Ok(opt): Result<Opt, _> = opt.try_into() else { return };
|
||||
|
||||
self.close(false);
|
||||
self.visible = true;
|
||||
|
|
@ -28,17 +28,26 @@ impl Input {
|
|||
self.position = opt.cfg.position;
|
||||
|
||||
// Typing
|
||||
self.callback = Some(opt.tx);
|
||||
self.realtime = opt.cfg.realtime;
|
||||
self.completion = opt.cfg.completion;
|
||||
self.tx = Some(opt.tx.clone());
|
||||
|
||||
// Shell
|
||||
self.highlight = opt.cfg.highlight;
|
||||
|
||||
// Reset snaps
|
||||
self.snaps.reset(opt.cfg.value, self.limit());
|
||||
// Reset input
|
||||
let ticket = self.ticket.clone();
|
||||
let cb: Box<dyn Fn(&str, &str)> = Box::new(move |before, after| {
|
||||
if opt.cfg.realtime {
|
||||
opt.tx.send(Err(InputError::Typed(format!("{before}{after}")))).ok();
|
||||
}
|
||||
|
||||
if opt.cfg.completion {
|
||||
opt.tx.send(Err(InputError::Completed(before.to_owned(), ticket.current()))).ok();
|
||||
}
|
||||
});
|
||||
self.inner = yazi_widgets::input::Input::new(opt.cfg.value, self.limit, cb);
|
||||
|
||||
// Set cursor after reset
|
||||
// TODO: remove this
|
||||
if let Some(cursor) = opt.cfg.cursor {
|
||||
self.snap_mut().cursor = cursor;
|
||||
self.move_(0);
|
||||
|
|
|
|||
|
|
@ -1,131 +1,31 @@
|
|||
use std::ops::Range;
|
||||
use std::{ops::{Deref, DerefMut}, rc::Rc};
|
||||
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use yazi_config::{INPUT, popup::Position};
|
||||
use yazi_plugin::CLIPBOARD;
|
||||
use yazi_shared::errors::InputError;
|
||||
|
||||
use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp};
|
||||
use yazi_config::popup::Position;
|
||||
use yazi_shared::{Ids, errors::InputError};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Input {
|
||||
pub(super) snaps: InputSnaps,
|
||||
pub ticket: usize,
|
||||
pub visible: bool,
|
||||
pub(super) inner: yazi_widgets::input::Input,
|
||||
|
||||
pub visible: bool,
|
||||
pub title: String,
|
||||
pub position: Position,
|
||||
|
||||
// Typing
|
||||
pub(super) callback: Option<UnboundedSender<Result<String, InputError>>>,
|
||||
pub(super) realtime: bool,
|
||||
pub(super) completion: bool,
|
||||
pub(super) tx: Option<UnboundedSender<Result<String, InputError>>>,
|
||||
pub(super) ticket: Rc<Ids>,
|
||||
|
||||
// Shell
|
||||
pub highlight: bool,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
#[inline]
|
||||
pub(super) fn limit(&self) -> usize {
|
||||
self.position.offset.width.saturating_sub(INPUT.border()) as usize
|
||||
}
|
||||
impl Deref for Input {
|
||||
type Target = yazi_widgets::input::Input;
|
||||
|
||||
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
|
||||
let old = self.snap().clone();
|
||||
let snap = self.snap_mut();
|
||||
|
||||
match snap.op {
|
||||
InputOp::None | InputOp::Select(_) => {
|
||||
snap.cursor = cursor;
|
||||
}
|
||||
InputOp::Delete(cut, insert, _) => {
|
||||
let range = snap.op.range(cursor, include).unwrap();
|
||||
let Range { start, end } = snap.idx(range.start)..snap.idx(range.end);
|
||||
|
||||
let drain = snap.value.drain(start.unwrap()..end.unwrap()).collect::<String>();
|
||||
if cut {
|
||||
futures::executor::block_on(CLIPBOARD.set(&drain));
|
||||
}
|
||||
|
||||
snap.op = InputOp::None;
|
||||
snap.mode = if insert { InputMode::Insert } else { InputMode::Normal };
|
||||
snap.cursor = range.start;
|
||||
}
|
||||
InputOp::Yank(_) => {
|
||||
let range = snap.op.range(cursor, include).unwrap();
|
||||
let Range { start, end } = snap.idx(range.start)..snap.idx(range.end);
|
||||
let yanked = &snap.value[start.unwrap()..end.unwrap()];
|
||||
|
||||
snap.op = InputOp::None;
|
||||
futures::executor::block_on(CLIPBOARD.set(yanked));
|
||||
}
|
||||
};
|
||||
|
||||
snap.cursor = snap.count().saturating_sub(snap.mode.delta()).min(snap.cursor);
|
||||
if snap == &old {
|
||||
return false;
|
||||
}
|
||||
if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
|
||||
self.snaps.tag(self.limit()).then(|| self.flush_value());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn flush_value(&mut self) {
|
||||
let Some(tx) = &self.callback else { return };
|
||||
self.ticket = self.ticket.wrapping_add(1);
|
||||
|
||||
if self.realtime {
|
||||
let value = self.snap().value.clone();
|
||||
tx.send(Err(InputError::Typed(value))).ok();
|
||||
}
|
||||
|
||||
if self.completion {
|
||||
let before = self.partition()[0].to_owned();
|
||||
tx.send(Err(InputError::Completed(before, self.ticket))).ok();
|
||||
}
|
||||
}
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
#[inline]
|
||||
pub fn value(&self) -> &str { self.snap().slice(self.snap().window(self.limit())) }
|
||||
|
||||
#[inline]
|
||||
pub fn mode(&self) -> InputMode { self.snap().mode }
|
||||
|
||||
#[inline]
|
||||
pub fn cursor(&self) -> u16 {
|
||||
let snap = self.snap();
|
||||
snap.slice(snap.offset..snap.cursor).width() as u16
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<Range<u16>> {
|
||||
let snap = self.snap();
|
||||
let start = snap.op.start()?;
|
||||
|
||||
let (start, end) =
|
||||
if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) };
|
||||
|
||||
let win = snap.window(self.limit());
|
||||
let Range { start, end } = start.max(win.start)..end.min(win.end);
|
||||
|
||||
let s = snap.slice(snap.offset..start).width() as u16;
|
||||
Some(s..s + snap.slice(start..end).width() as u16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn partition(&self) -> [&str; 2] {
|
||||
let snap = self.snap();
|
||||
let idx = snap.idx(snap.cursor).unwrap();
|
||||
[&snap.value[..idx], &snap.value[idx..]]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn snap(&self) -> &InputSnap { self.snaps.current() }
|
||||
|
||||
#[inline]
|
||||
pub(super) fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() }
|
||||
impl DerefMut for Input {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
yazi_macro::mod_pub!(commands);
|
||||
|
||||
yazi_macro::mod_flat!(input mode op snap snaps);
|
||||
yazi_macro::mod_flat!(input);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
|||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
yazi-widgets = { path = "../yazi-widgets", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ use std::sync::atomic::Ordering;
|
|||
use anyhow::Result;
|
||||
use crossterm::event::KeyEvent;
|
||||
use yazi_config::keymap::Key;
|
||||
use yazi_core::input::InputMode;
|
||||
use yazi_macro::emit;
|
||||
use yazi_shared::event::{CmdCow, Event, NEED_RENDER};
|
||||
use yazi_widgets::input::InputMode;
|
||||
|
||||
use crate::{Ctx, Executor, Router, Signals, Term, lives::Lives};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use yazi_core::input::InputMode;
|
||||
use yazi_shared::{Layer, event::CmdCow};
|
||||
use yazi_widgets::input::InputMode;
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use anyhow::{Result, bail};
|
|||
use ratatui::{buffer::Buffer, layout::Rect, text::Line, widgets::{Block, BorderType, Paragraph, Widget}};
|
||||
use syntect::easy::HighlightLines;
|
||||
use yazi_config::{PREVIEW, THEME};
|
||||
use yazi_core::input::InputMode;
|
||||
use yazi_plugin::external::Highlighter;
|
||||
use yazi_widgets::input::InputMode;
|
||||
|
||||
use crate::{Ctx, Term};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use yazi_macro::emit;
|
||||
use yazi_shared::event::Cmd;
|
||||
use yazi_shared::{Id, event::Cmd};
|
||||
|
||||
pub struct CmpProxy;
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ impl CmpProxy {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn trigger(word: &str, ticket: usize) {
|
||||
pub fn trigger(word: &str, ticket: Id) {
|
||||
emit!(Call(Cmd::args("cmp:trigger", &[word]).with("ticket", ticket)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::{error::Error, fmt::{self, Display}};
|
||||
|
||||
use crate::Id;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InputError {
|
||||
Typed(String),
|
||||
Completed(String, usize),
|
||||
Completed(String, Id),
|
||||
Canceled(String),
|
||||
}
|
||||
|
||||
|
|
|
|||
23
yazi-widgets/Cargo.toml
Normal file
23
yazi-widgets/Cargo.toml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "yazi-widgets"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
description = "Yazi user interface widgets"
|
||||
homepage = "https://yazi-rs.github.io"
|
||||
repository = "https://github.com/sxyazi/yazi"
|
||||
rust-version = "1.83.0"
|
||||
|
||||
[dependencies]
|
||||
yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
futures = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
unicode-width = { workspace = true }
|
||||
|
|
@ -12,15 +12,15 @@ const SEPARATOR: [char; 2] = ['/', '\\'];
|
|||
const SEPARATOR: char = std::path::MAIN_SEPARATOR;
|
||||
|
||||
struct Opt {
|
||||
word: Cow<'static, str>,
|
||||
ticket: usize,
|
||||
word: Cow<'static, str>,
|
||||
_ticket: usize, // FIXME
|
||||
}
|
||||
|
||||
impl From<CmdCow> for Opt {
|
||||
fn from(mut c: CmdCow) -> Self {
|
||||
Self {
|
||||
word: c.take_first_str().unwrap_or_default(),
|
||||
ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0),
|
||||
word: c.take_first_str().unwrap_or_default(),
|
||||
_ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,11 +28,7 @@ impl From<CmdCow> for Opt {
|
|||
impl Input {
|
||||
#[yazi_codegen::command]
|
||||
pub fn complete(&mut self, opt: Opt) {
|
||||
if self.ticket != opt.ticket {
|
||||
return;
|
||||
}
|
||||
|
||||
let [before, after] = self.partition();
|
||||
let (before, after) = self.partition();
|
||||
let new = if let Some((prefix, _)) = before.rsplit_once(SEPARATOR) {
|
||||
format!("{prefix}/{}{after}", opt.word).replace(SEPARATOR, MAIN_SEPARATOR_STR)
|
||||
} else {
|
||||
27
yazi-widgets/src/input/commands/escape.rs
Normal file
27
yazi-widgets/src/input/commands/escape.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use crate::input::{Input, InputMode, op::InputOp};
|
||||
|
||||
struct Opt;
|
||||
|
||||
impl From<()> for Opt {
|
||||
fn from(_: ()) -> Self { Self }
|
||||
}
|
||||
|
||||
impl Input {
|
||||
#[yazi_codegen::command]
|
||||
pub fn escape(&mut self, _: Opt) {
|
||||
let snap = self.snap_mut();
|
||||
match snap.mode {
|
||||
InputMode::Normal => {
|
||||
snap.op = InputOp::None;
|
||||
}
|
||||
InputMode::Insert => {
|
||||
snap.mode = InputMode::Normal;
|
||||
self.move_(-1);
|
||||
}
|
||||
InputMode::Replace => {
|
||||
snap.mode = InputMode::Normal;
|
||||
}
|
||||
}
|
||||
self.snaps.tag(self.limit);
|
||||
}
|
||||
}
|
||||
1
yazi-widgets/src/input/commands/mod.rs
Normal file
1
yazi-widgets/src/input/commands/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(backspace backward complete delete escape forward insert kill move_ paste redo replace type_ undo visual yank);
|
||||
|
|
@ -33,7 +33,7 @@ impl Input {
|
|||
|
||||
render!(self.handle_op(opt.step.cursor(snap), false));
|
||||
|
||||
let (limit, snap) = (self.limit(), self.snap_mut());
|
||||
let (limit, snap) = (self.limit, self.snap_mut());
|
||||
if snap.offset > snap.cursor {
|
||||
snap.offset = snap.cursor;
|
||||
} else if snap.value.is_empty() {
|
||||
|
|
@ -27,6 +27,6 @@ impl Input {
|
|||
}
|
||||
|
||||
render!();
|
||||
self.snaps.tag(self.limit()).then(|| self.flush_value());
|
||||
self.snaps.tag(self.limit).then(|| self.flush_value());
|
||||
}
|
||||
}
|
||||
108
yazi-widgets/src/input/input.rs
Normal file
108
yazi-widgets/src/input/input.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use yazi_plugin::CLIPBOARD;
|
||||
|
||||
use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Input {
|
||||
pub snaps: InputSnaps,
|
||||
pub limit: usize,
|
||||
pub callback: Option<Box<dyn Fn(&str, &str)>>,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn new(value: String, limit: usize, callback: Box<dyn Fn(&str, &str)>) -> Self {
|
||||
Self { snaps: InputSnaps::new(value, limit), limit, callback: Some(callback) }
|
||||
}
|
||||
|
||||
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
|
||||
let old = self.snap().clone();
|
||||
let snap = self.snap_mut();
|
||||
|
||||
match snap.op {
|
||||
InputOp::None | InputOp::Select(_) => {
|
||||
snap.cursor = cursor;
|
||||
}
|
||||
InputOp::Delete(cut, insert, _) => {
|
||||
let range = snap.op.range(cursor, include).unwrap();
|
||||
let Range { start, end } = snap.idx(range.start)..snap.idx(range.end);
|
||||
|
||||
let drain = snap.value.drain(start.unwrap()..end.unwrap()).collect::<String>();
|
||||
if cut {
|
||||
futures::executor::block_on(CLIPBOARD.set(&drain));
|
||||
}
|
||||
|
||||
snap.op = InputOp::None;
|
||||
snap.mode = if insert { InputMode::Insert } else { InputMode::Normal };
|
||||
snap.cursor = range.start;
|
||||
}
|
||||
InputOp::Yank(_) => {
|
||||
let range = snap.op.range(cursor, include).unwrap();
|
||||
let Range { start, end } = snap.idx(range.start)..snap.idx(range.end);
|
||||
let yanked = &snap.value[start.unwrap()..end.unwrap()];
|
||||
|
||||
snap.op = InputOp::None;
|
||||
futures::executor::block_on(CLIPBOARD.set(yanked));
|
||||
}
|
||||
};
|
||||
|
||||
snap.cursor = snap.count().saturating_sub(snap.mode.delta()).min(snap.cursor);
|
||||
if snap == &old {
|
||||
return false;
|
||||
}
|
||||
if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
|
||||
self.snaps.tag(self.limit).then(|| self.flush_value());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) fn flush_value(&mut self) {
|
||||
if let Some(cb) = &self.callback {
|
||||
let (before, after) = self.partition();
|
||||
cb(before, after);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
#[inline]
|
||||
pub fn value(&self) -> &str { self.snap().slice(self.snap().window(self.limit)) }
|
||||
|
||||
#[inline]
|
||||
pub fn mode(&self) -> InputMode { self.snap().mode }
|
||||
|
||||
#[inline]
|
||||
pub fn cursor(&self) -> u16 {
|
||||
let snap = self.snap();
|
||||
snap.slice(snap.offset..snap.cursor).width() as u16
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<Range<u16>> {
|
||||
let snap = self.snap();
|
||||
let start = snap.op.start()?;
|
||||
|
||||
let (start, end) =
|
||||
if start < snap.cursor { (start, snap.cursor) } else { (snap.cursor + 1, start + 1) };
|
||||
|
||||
let win = snap.window(self.limit);
|
||||
let Range { start, end } = start.max(win.start)..end.min(win.end);
|
||||
|
||||
let s = snap.slice(snap.offset..start).width() as u16;
|
||||
Some(s..s + snap.slice(start..end).width() as u16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn partition(&self) -> (&str, &str) {
|
||||
let snap = self.snap();
|
||||
let idx = snap.idx(snap.cursor).unwrap();
|
||||
(&snap.value[..idx], &snap.value[idx..])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn snap(&self) -> &InputSnap { self.snaps.current() }
|
||||
|
||||
#[inline]
|
||||
pub fn snap_mut(&mut self) -> &mut InputSnap { self.snaps.current_mut() }
|
||||
}
|
||||
3
yazi-widgets/src/input/mod.rs
Normal file
3
yazi-widgets/src/input/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
yazi_macro::mod_pub!(commands);
|
||||
|
||||
yazi_macro::mod_flat!(input mode op snap snaps);
|
||||
|
|
@ -5,14 +5,14 @@ use unicode_width::UnicodeWidthChar;
|
|||
use super::{InputMode, InputOp};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub(super) struct InputSnap {
|
||||
pub(super) value: String,
|
||||
pub struct InputSnap {
|
||||
pub value: String,
|
||||
|
||||
pub(super) op: InputOp,
|
||||
pub op: InputOp,
|
||||
|
||||
pub(super) mode: InputMode,
|
||||
pub(super) offset: usize,
|
||||
pub(super) cursor: usize,
|
||||
pub mode: InputMode,
|
||||
pub offset: usize,
|
||||
pub cursor: usize,
|
||||
}
|
||||
|
||||
impl InputSnap {
|
||||
|
|
@ -22,7 +22,7 @@ impl InputSnap {
|
|||
|
||||
op: Default::default(),
|
||||
|
||||
mode: Default::default(),
|
||||
mode: Default::default(),
|
||||
offset: usize::MAX,
|
||||
cursor: usize::MAX,
|
||||
};
|
||||
|
|
@ -3,19 +3,16 @@ use std::mem;
|
|||
use super::InputSnap;
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
pub(super) struct InputSnaps {
|
||||
pub struct InputSnaps {
|
||||
idx: usize,
|
||||
versions: Vec<InputSnap>,
|
||||
current: InputSnap,
|
||||
}
|
||||
|
||||
impl InputSnaps {
|
||||
#[inline]
|
||||
pub(super) fn reset(&mut self, value: String, limit: usize) {
|
||||
self.idx = 0;
|
||||
self.versions.clear();
|
||||
self.versions.push(InputSnap::new(value, limit));
|
||||
self.current = self.versions[0].clone();
|
||||
pub fn new(value: String, limit: usize) -> Self {
|
||||
let current = InputSnap::new(value, limit);
|
||||
Self { idx: 0, versions: vec![current.clone()], current }
|
||||
}
|
||||
|
||||
pub(super) fn tag(&mut self, limit: usize) -> bool {
|
||||
|
|
@ -65,7 +62,7 @@ impl InputSnaps {
|
|||
|
||||
impl InputSnaps {
|
||||
#[inline]
|
||||
pub(super) fn current(&self) -> &InputSnap { &self.current }
|
||||
pub fn current(&self) -> &InputSnap { &self.current }
|
||||
|
||||
#[inline]
|
||||
pub(super) fn current_mut(&mut self) -> &mut InputSnap { &mut self.current }
|
||||
1
yazi-widgets/src/lib.rs
Normal file
1
yazi-widgets/src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_pub!(input);
|
||||
Loading…
Add table
Reference in a new issue