mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-22 23:31:05 +00:00
feat: hover cursor over the new file after copying/cutting/linking/hardlinking (#3846)
This commit is contained in:
parent
ad655eda52
commit
0cedbd9c7b
96 changed files with 383 additions and 360 deletions
|
|
@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
|
||||
- Custom tab name ([#3666])
|
||||
- New `--in` for `search` action to set search directory ([#3696])
|
||||
- Hover cursor over the new file after copying/cutting/linking/hardlinking ([#3846])
|
||||
- Multi-file spotter ([#3733])
|
||||
- Vim-like `lua` action that runs an inline Lua snippet ([#3813])
|
||||
- Certificate authentication for SFTP VFS provider ([#3716])
|
||||
|
|
@ -1694,3 +1695,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
[#3792]: https://github.com/sxyazi/yazi/pull/3792
|
||||
[#3804]: https://github.com/sxyazi/yazi/pull/3804
|
||||
[#3813]: https://github.com/sxyazi/yazi/pull/3813
|
||||
[#3846]: https://github.com/sxyazi/yazi/pull/3846
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ debug = false
|
|||
[workspace.dependencies]
|
||||
ansi-to-tui = "8.0.1"
|
||||
anyhow = "1.0.102"
|
||||
arc-swap = "1.9.0"
|
||||
base64 = "0.22.1"
|
||||
bitflags = { version = "2.11.0", features = [ "serde" ] }
|
||||
chrono = "0.4.44"
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ impl Actor for Deprecate {
|
|||
|
||||
const NAME: &str = "deprecate";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(notify:push, cx, MessageOpt {
|
||||
title: "Deprecated API".to_owned(),
|
||||
content: opt.content.into_owned(),
|
||||
content: form.content.into_owned(),
|
||||
level: MessageLevel::Warn,
|
||||
timeout: std::time::Duration::from_secs(20),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ impl Actor for Lua {
|
|||
|
||||
const NAME: &str = "lua";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let chunk = LUA.load(&*opt.code).set_name("anonymous");
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let chunk = LUA.load(&*form.code).set_name("anonymous");
|
||||
let result = Lives::scope(cx.core, || {
|
||||
runtime_scope!(LUA, "inline", Sendable::value_to_data(&LUA, chunk.eval()?))
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ impl Actor for Mouse {
|
|||
|
||||
const NAME: &str = "mouse";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let event = yazi_binding::MouseEvent::from(opt.event);
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let event = yazi_binding::MouseEvent::from(form.event);
|
||||
|
||||
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||
let area = yazi_binding::elements::Rect::from(size);
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ impl Actor for Reflow {
|
|||
|
||||
const NAME: &str = "reflow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
|
||||
let mut layout = LAYOUT.get();
|
||||
|
||||
let result = Lives::scope(cx.core, || {
|
||||
let comps = (opt.reflow)((Position::ORIGIN, size).into())?;
|
||||
let comps = (form.reflow)((Position::ORIGIN, size).into())?;
|
||||
|
||||
for v in comps.sequence_values::<Value>() {
|
||||
let Value::Table(t) = v? else {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ impl Actor for Resize {
|
|||
|
||||
const NAME: &str = "resize";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
act!(app:reflow, cx, opt)?;
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(app:reflow, cx, form)?;
|
||||
|
||||
cx.current_mut().arrow(0);
|
||||
cx.parent_mut().map(|f| f.arrow(0));
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@ impl Actor for Resume {
|
|||
|
||||
const NAME: &str = "resume";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.active_mut().preview.reset();
|
||||
*cx.term = Some(Term::start()?);
|
||||
|
||||
// While the app resumes, it's possible that the terminal size has changed.
|
||||
// We need to trigger a resize, and render the UI based on the resized area.
|
||||
act!(app:resize, cx, opt.reflow)?;
|
||||
act!(app:resize, cx, form.reflow)?;
|
||||
|
||||
opt.tx.send((true, opt.token))?;
|
||||
form.tx.send((true, form.replier))?;
|
||||
|
||||
act!(app:title, cx).ok();
|
||||
succ!(render!());
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl Actor for Stop {
|
|||
|
||||
const NAME: &str = "stop";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.active_mut().preview.reset_image();
|
||||
|
||||
// We need to destroy the `term` first before stopping the `signals`
|
||||
|
|
@ -20,7 +20,7 @@ impl Actor for Stop {
|
|||
// while the app is being suspended.
|
||||
*cx.term = None;
|
||||
|
||||
opt.tx.send((false, opt.token))?;
|
||||
form.tx.send((false, form.replier))?;
|
||||
|
||||
succ!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@ impl Actor for Title {
|
|||
|
||||
const NAME: &str = "title";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let s = opt.value.unwrap_or_else(|| format!("Yazi: {}", cx.tab().name()).into());
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let s = form.value.unwrap_or_else(|| format!("Yazi: {}", cx.tab().name()).into());
|
||||
execute!(TTY.writer(), SetTitle(&s))?;
|
||||
|
||||
yazi_term::STATE.set(TermState { title: !s.is_empty(), ..yazi_term::STATE.get() });
|
||||
succ!()
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _opt: &Self::Form) -> Option<SparkKind> {
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
match cx.source() {
|
||||
Source::Ind => Some(SparkKind::IndAppTitle),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ impl Actor for UpdateProgress {
|
|||
|
||||
const NAME: &str = "update_progress";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
// Update the progress of all tasks.
|
||||
let tasks = &mut cx.tasks;
|
||||
let progressed = tasks.summary != opt.summary;
|
||||
tasks.summary = opt.summary;
|
||||
let progressed = tasks.summary != form.summary;
|
||||
tasks.summary = form.summary;
|
||||
|
||||
// If the task manager is visible, update the snaps with a full render.
|
||||
if tasks.visible {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.cmp.scroll(opt.step)));
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.cmp.scroll(form.step)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ impl Actor for Close {
|
|||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let cmp = &mut cx.cmp;
|
||||
if let Some(item) = cmp.selected().filter(|_| opt.submit).cloned() {
|
||||
if let Some(item) = cmp.selected().filter(|_| form.submit).cloned() {
|
||||
return act!(input:complete, cx, CompleteOpt { name: item.name, is_dir: item.is_dir, ticket: cmp.ticket });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@ impl Actor for Trigger {
|
|||
|
||||
const NAME: &str = "trigger";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
if opt.ticket.is_some_and(|t| t != cx.cmp.ticket) {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.ticket.is_some_and(|t| t != cx.cmp.ticket) {
|
||||
succ!();
|
||||
} else if opt.ticket.is_none() {
|
||||
} else if form.ticket.is_none() {
|
||||
cx.cmp.ticket = cx.input.ticket.current();
|
||||
}
|
||||
|
||||
cx.cmp.handle.take().map(|h| h.abort());
|
||||
let Some((parent, word)) = Self::split_url(&opt.word) else {
|
||||
let Some((parent, word)) = Self::split_url(&form.word) else {
|
||||
return act!(cmp:close, cx, false);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let confirm = &mut cx.core.confirm;
|
||||
|
||||
let area = cx.core.mgr.area(confirm.position);
|
||||
let len = confirm.list.line_count(area.width);
|
||||
|
||||
let old = confirm.offset;
|
||||
confirm.offset = opt.step.add(confirm.offset, len, area.height as _);
|
||||
confirm.offset = form.step.add(confirm.offset, len, area.height as _);
|
||||
|
||||
succ!(render!(old != confirm.offset));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ impl Actor for Close {
|
|||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
cx.confirm.token.complete(opt.submit);
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.confirm.token.complete(form.submit);
|
||||
cx.confirm.visible = false;
|
||||
succ!(render!());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ impl Actor for Show {
|
|||
|
||||
const NAME: &str = "show";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(confirm:close, cx)?;
|
||||
|
||||
let confirm = &mut cx.confirm;
|
||||
confirm.title = opt.cfg.title;
|
||||
confirm.body = opt.cfg.body;
|
||||
confirm.list = opt.cfg.list;
|
||||
confirm.title = form.cfg.title;
|
||||
confirm.body = form.cfg.body;
|
||||
confirm.list = form.cfg.list;
|
||||
|
||||
confirm.position = opt.cfg.position;
|
||||
confirm.position = form.cfg.position;
|
||||
confirm.offset = 0;
|
||||
|
||||
confirm.token = opt.token;
|
||||
confirm.token = form.token;
|
||||
confirm.visible = true;
|
||||
|
||||
succ!(render!());
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.help.scroll(opt.step)));
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.help.scroll(form.step)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ impl Actor for Toggle {
|
|||
|
||||
const NAME: &str = "toggle";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let help = &mut cx.help;
|
||||
|
||||
help.visible = !help.visible;
|
||||
help.layer = opt.layer;
|
||||
help.layer = form.layer;
|
||||
|
||||
help.keyword = String::new();
|
||||
help.in_filter = None;
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ impl Actor for Close {
|
|||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let input = &mut cx.input;
|
||||
input.visible = false;
|
||||
input.ticket.next();
|
||||
|
||||
if let Some(tx) = input.tx.take() {
|
||||
let value = input.snap().value.clone();
|
||||
_ = tx.send(if opt.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) });
|
||||
_ = tx.send(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) });
|
||||
}
|
||||
|
||||
act!(cmp:close, cx)?;
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ impl Actor for Complete {
|
|||
|
||||
const NAME: &str = "complete";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let input = &mut cx.input;
|
||||
if !input.visible || input.ticket.current() != opt.ticket {
|
||||
if !input.visible || input.ticket.current() != form.ticket {
|
||||
succ!();
|
||||
}
|
||||
|
||||
act!(complete, input, opt)
|
||||
act!(complete, input, form)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ impl Actor for Show {
|
|||
|
||||
const NAME: &str = "show";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(input:close, cx)?;
|
||||
|
||||
let input = &mut cx.input;
|
||||
input.visible = true;
|
||||
input.title = opt.cfg.title.clone();
|
||||
input.position = opt.cfg.position;
|
||||
*input.deref_mut() = yazi_widgets::input::Input::new(opt)?;
|
||||
input.title = form.cfg.title.clone();
|
||||
input.position = form.cfg.position;
|
||||
*input.deref_mut() = yazi_widgets::input::Input::new(form)?;
|
||||
|
||||
succ!(render!());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
if !tab.current.arrow(opt.step) {
|
||||
if !tab.current.arrow(form.step) {
|
||||
succ!();
|
||||
}
|
||||
|
||||
// Retrace
|
||||
tab.current.retrace();
|
||||
|
||||
// Visual selection
|
||||
if let Some((start, items)) = tab.mode.visual_mut() {
|
||||
let end = tab.current.cursor;
|
||||
|
|
@ -28,7 +31,7 @@ impl Actor for Arrow {
|
|||
act!(mgr:peek, cx)?;
|
||||
act!(mgr:watch, cx)?;
|
||||
|
||||
// cx.tasks.scheduler.batch.next(); // FIXME: clear user action
|
||||
cx.tasks.scheduler.behavior.reset();
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ impl Actor for BulkExit {
|
|||
|
||||
const NAME: &str = "bulk_exit";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
cx.mgr.batcher.decide(opt.target, opt.accept);
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.mgr.batcher.decide(form.target, form.accept);
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ impl Actor for Cd {
|
|||
|
||||
const NAME: &str = "cd";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
if opt.interactive {
|
||||
if form.interactive {
|
||||
return Self::cd_interactive(cx);
|
||||
}
|
||||
|
||||
let tab = cx.tab_mut();
|
||||
if opt.target == *tab.cwd() {
|
||||
if form.target == *tab.cwd() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
|
|
@ -39,12 +39,12 @@ impl Actor for Cd {
|
|||
}
|
||||
|
||||
// Current
|
||||
let rep = tab.history.remove_or(&opt.target);
|
||||
let rep = tab.history.remove_or(&form.target);
|
||||
let rep = mem::replace(&mut tab.current, rep);
|
||||
tab.history.insert(rep.url.clone(), rep);
|
||||
|
||||
// Parent
|
||||
if let Some(parent) = opt.target.parent() {
|
||||
if let Some(parent) = form.target.parent() {
|
||||
tab.parent = Some(tab.history.remove_or(parent));
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ impl Actor for Cd {
|
|||
act!(mgr:sort, cx).ok();
|
||||
act!(mgr:hover, cx)?;
|
||||
act!(mgr:refresh, cx)?;
|
||||
act!(mgr:stash, cx, opt).ok();
|
||||
act!(mgr:stash, cx, form).ok();
|
||||
act!(app:title, cx).ok();
|
||||
succ!(render!());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ impl Actor for Copy {
|
|||
|
||||
const NAME: &str = "copy";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
let mut s = Vec::<u8>::new();
|
||||
let mut it = if opt.hovered {
|
||||
let mut it = if form.hovered {
|
||||
Box::new(cx.hovered().map(|h| &h.url).into_iter())
|
||||
} else {
|
||||
cx.tab().selected_or_hovered()
|
||||
|
|
@ -25,23 +25,23 @@ impl Actor for Copy {
|
|||
.peekable();
|
||||
|
||||
while let Some(u) = it.next() {
|
||||
match opt.r#type.as_ref() {
|
||||
match form.r#type.as_ref() {
|
||||
// TODO: rename to "url"
|
||||
"path" => {
|
||||
s.extend_from_slice(&opt.separator.transform(&u.to_strand()));
|
||||
s.extend_from_slice(&form.separator.transform(&u.to_strand()));
|
||||
}
|
||||
"dirname" => {
|
||||
if let Some(p) = u.parent() {
|
||||
s.extend_from_slice(&opt.separator.transform(&p.to_strand()));
|
||||
s.extend_from_slice(&form.separator.transform(&p.to_strand()));
|
||||
}
|
||||
}
|
||||
"filename" => {
|
||||
s.extend_from_slice(&opt.separator.transform(&u.name().unwrap_or_default()));
|
||||
s.extend_from_slice(&form.separator.transform(&u.name().unwrap_or_default()));
|
||||
}
|
||||
"name_without_ext" => {
|
||||
s.extend_from_slice(&opt.separator.transform(&u.stem().unwrap_or_default()));
|
||||
s.extend_from_slice(&form.separator.transform(&u.stem().unwrap_or_default()));
|
||||
}
|
||||
_ => bail!("Unknown copy type: {}", opt.r#type),
|
||||
_ => bail!("Unknown copy type: {}", form.r#type),
|
||||
};
|
||||
if it.peek().is_some() {
|
||||
s.push(b'\n');
|
||||
|
|
@ -49,8 +49,8 @@ impl Actor for Copy {
|
|||
}
|
||||
|
||||
// Copy the CWD path regardless even if the directory is empty
|
||||
if s.is_empty() && opt.r#type == "dirname" {
|
||||
s.extend_from_slice(&opt.separator.transform(&cx.cwd().to_strand()));
|
||||
if s.is_empty() && form.r#type == "dirname" {
|
||||
s.extend_from_slice(&form.separator.transform(&cx.cwd().to_strand()));
|
||||
}
|
||||
|
||||
futures::executor::block_on(CLIPBOARD.set(s));
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ impl Actor for Create {
|
|||
|
||||
const NAME: &str = "create";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let cwd = cx.cwd().to_owned();
|
||||
let mut input = InputProxy::show(InputCfg::create(opt.dir));
|
||||
let mut input = InputProxy::show(InputCfg::create(form.dir));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let Some(InputEvent::Submit(name)) = input.recv().await else { return };
|
||||
|
|
@ -32,14 +32,14 @@ impl Actor for Create {
|
|||
return;
|
||||
};
|
||||
|
||||
if !opt.force
|
||||
if !form.force
|
||||
&& maybe_exists(&new).await
|
||||
&& !ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = Self::r#do(new, opt.dir || name.ends_with('/') || name.ends_with('\\')).await;
|
||||
_ = Self::r#do(new, form.dir || name.ends_with('/') || name.ends_with('\\')).await;
|
||||
});
|
||||
succ!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,15 +20,15 @@ impl Actor for Download {
|
|||
|
||||
const NAME: &str = "download";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let cwd = cx.cwd().clone();
|
||||
let scheduler = cx.tasks.scheduler.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
Self::prepare(&opt.urls).await;
|
||||
Self::prepare(&form.urls).await;
|
||||
|
||||
let mut wg1 = FuturesUnordered::new();
|
||||
for url in opt.urls {
|
||||
for url in form.urls {
|
||||
let done = scheduler.file_download(url.to_owned());
|
||||
wg1.push(async move { (done.future().await, url) });
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ impl Actor for Download {
|
|||
if futures::future::join_all(wg2).await.into_iter().any(|b| !b) {
|
||||
return;
|
||||
}
|
||||
if opt.open && !urls.is_empty() {
|
||||
if form.open && !urls.is_empty() {
|
||||
MgrProxy::open(OpenOpt {
|
||||
cwd: Some(cwd.into()),
|
||||
targets: urls,
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ impl Actor for Escape {
|
|||
|
||||
const NAME: &str = "escape";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
if opt.is_empty() {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.is_empty() {
|
||||
_ = act!(mgr:escape_find, cx)? != false
|
||||
|| act!(mgr:escape_visual, cx)? != false
|
||||
|| act!(mgr:escape_filter, cx)? != false
|
||||
|
|
@ -23,19 +23,19 @@ impl Actor for Escape {
|
|||
succ!();
|
||||
}
|
||||
|
||||
if opt.contains(EscapeForm::FIND) {
|
||||
if form.contains(EscapeForm::FIND) {
|
||||
act!(mgr:escape_find, cx)?;
|
||||
}
|
||||
if opt.contains(EscapeForm::VISUAL) {
|
||||
if form.contains(EscapeForm::VISUAL) {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
}
|
||||
if opt.contains(EscapeForm::FILTER) {
|
||||
if form.contains(EscapeForm::FILTER) {
|
||||
act!(mgr:escape_filter, cx)?;
|
||||
}
|
||||
if opt.contains(EscapeForm::SELECT) {
|
||||
if form.contains(EscapeForm::SELECT) {
|
||||
act!(mgr:escape_select, cx)?;
|
||||
}
|
||||
if opt.contains(EscapeForm::SEARCH) {
|
||||
if form.contains(EscapeForm::SEARCH) {
|
||||
act!(mgr:escape_search, cx)?;
|
||||
}
|
||||
succ!();
|
||||
|
|
|
|||
|
|
@ -20,15 +20,15 @@ impl Actor for Find {
|
|||
|
||||
const NAME: &str = "find";
|
||||
|
||||
fn act(_: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let input = InputProxy::show(InputCfg::find(opt.prev));
|
||||
fn act(_: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let input = InputProxy::show(InputCfg::find(form.prev));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));
|
||||
pin!(rx);
|
||||
|
||||
while let Some(InputEvent::Submit(s) | InputEvent::Type(s)) = rx.next().await {
|
||||
MgrProxy::find_do(FindDoOpt { query: s.into(), prev: opt.prev, case: opt.case });
|
||||
MgrProxy::find_do(FindDoOpt { query: s.into(), prev: form.prev, case: form.case });
|
||||
}
|
||||
});
|
||||
succ!();
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ impl Actor for FindArrow {
|
|||
|
||||
const NAME: &str = "find_arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
let Some(finder) = &mut tab.finder else { succ!() };
|
||||
|
||||
render!(finder.catchup(&tab.current));
|
||||
let offset = if opt.prev {
|
||||
let offset = if form.prev {
|
||||
finder.prev(&tab.current.files, tab.current.cursor, false)
|
||||
} else {
|
||||
finder.next(&tab.current.files, tab.current.cursor, false)
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ impl Actor for Hardlink {
|
|||
|
||||
const NAME: &str = "hardlink";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let mgr = &mut cx.core.mgr;
|
||||
let tab = &mgr.tabs[cx.tab];
|
||||
|
||||
if !mgr.yanked.cut {
|
||||
cx.core.tasks.file_hardlink(&mgr.yanked, tab.cwd(), opt.force, opt.follow);
|
||||
cx.core.tasks.file_hardlink(&mgr.yanked, tab.cwd(), form.force, form.follow);
|
||||
}
|
||||
|
||||
succ!();
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ impl Actor for Hidden {
|
|||
|
||||
const NAME: &str = "hidden";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let state = opt.state.bool(cx.tab().pref.show_hidden);
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let state = form.state.bool(cx.tab().pref.show_hidden);
|
||||
cx.tab_mut().pref.show_hidden = state;
|
||||
|
||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl Actor for Hover {
|
|||
|
||||
const NAME: &str = "hover";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = tab!(cx);
|
||||
|
||||
// Parent should always track CWD
|
||||
|
|
@ -22,16 +22,16 @@ impl Actor for Hover {
|
|||
}
|
||||
|
||||
// Repos CWD
|
||||
render!(tab.current.repos(opt.urn.as_ref().map(Into::into)));
|
||||
render!(tab.current.repos(form.urn.as_ref().map(Into::into)));
|
||||
|
||||
// Turn on tracing
|
||||
if let (Some(h), Some(u)) = (tab.hovered(), opt.urn)
|
||||
if let (Some(h), Some(u)) = (tab.hovered(), form.urn)
|
||||
&& h.urn() == u
|
||||
{
|
||||
// `hover(Some)` occurs after user actions, such as create, rename, reveal, etc.
|
||||
// At this point, it's intuitive to track the file location regardless.
|
||||
tab.current.trace = Some(u.clone());
|
||||
// cx.tasks.scheduler.batch.next();// FIXME: clear user action
|
||||
cx.tasks.scheduler.behavior.reset();
|
||||
}
|
||||
|
||||
// Publish through DDS
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ impl Actor for Linemode {
|
|||
|
||||
const NAME: &str = "linemode";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
|
||||
if opt.new != tab.pref.linemode {
|
||||
tab.pref.linemode = opt.new.into_owned();
|
||||
if form.new != tab.pref.linemode {
|
||||
tab.pref.linemode = form.new.into_owned();
|
||||
render!();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ impl Actor for Link {
|
|||
|
||||
const NAME: &str = "link";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let mgr = &mut cx.core.mgr;
|
||||
let tab = &mgr.tabs[cx.tab];
|
||||
|
||||
if !mgr.yanked.cut {
|
||||
cx.core.tasks.file_link(&mgr.yanked, tab.cwd(), opt.relative, opt.force);
|
||||
cx.core.tasks.file_link(&mgr.yanked, tab.cwd(), form.relative, form.force);
|
||||
}
|
||||
|
||||
succ!();
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ impl Actor for Paste {
|
|||
|
||||
const NAME: &str = "paste";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let mgr = &mut cx.core.mgr;
|
||||
let tab = &mgr.tabs[cx.tab];
|
||||
|
||||
let dest = tab.cwd();
|
||||
if mgr.yanked.cut {
|
||||
cx.core.tasks.file_cut(&mgr.yanked, dest, opt.force);
|
||||
cx.core.tasks.file_cut(&mgr.yanked, dest, form.force);
|
||||
|
||||
mgr.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(&*mgr.yanked));
|
||||
act!(mgr:unyank, cx)
|
||||
} else {
|
||||
succ!(cx.core.tasks.file_copy(&mgr.yanked, dest, opt.force, opt.follow));
|
||||
succ!(cx.core.tasks.file_copy(&mgr.yanked, dest, form.force, form.follow));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl Actor for Peek {
|
|||
|
||||
const NAME: &str = "peek";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(hovered) = cx.hovered().cloned() else {
|
||||
succ!(cx.tab_mut().preview.reset());
|
||||
};
|
||||
|
|
@ -33,13 +33,13 @@ impl Actor for Peek {
|
|||
cx.tab_mut().preview.folder_lock = None;
|
||||
}
|
||||
|
||||
if matches!(opt.only_if, Some(u) if u != hovered.url) {
|
||||
if matches!(form.only_if, Some(u) if u != hovered.url) {
|
||||
succ!();
|
||||
}
|
||||
|
||||
if let Some(skip) = opt.skip {
|
||||
if let Some(skip) = form.skip {
|
||||
let preview = &mut cx.tab_mut().preview;
|
||||
if opt.upper_bound {
|
||||
if form.upper_bound {
|
||||
preview.skip = preview.skip.min(skip);
|
||||
} else {
|
||||
preview.skip = skip;
|
||||
|
|
@ -47,9 +47,9 @@ impl Actor for Peek {
|
|||
}
|
||||
|
||||
if hovered.is_dir() {
|
||||
cx.tab_mut().preview.go_folder(hovered, folder.map(|(_, cha)| cha), mime, opt.force);
|
||||
cx.tab_mut().preview.go_folder(hovered, folder.map(|(_, cha)| cha), mime, form.force);
|
||||
} else {
|
||||
cx.tab_mut().preview.go(hovered, mime, opt.force);
|
||||
cx.tab_mut().preview.go(hovered, mime, form.force);
|
||||
}
|
||||
succ!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ impl Actor for Quit {
|
|||
succ!();
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _opt: &Self::Form) -> Option<SparkKind> {
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
Some(SparkKind::KeyQuit).filter(|_| cx.source().is_key())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,30 +14,30 @@ impl Actor for Remove {
|
|||
|
||||
const NAME: &str = "remove";
|
||||
|
||||
fn act(cx: &mut Ctx, mut opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, mut form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
opt.targets = if opt.hovered {
|
||||
form.targets = if form.hovered {
|
||||
cx.hovered().map_or(vec![], |h| vec![h.url.clone()])
|
||||
} else {
|
||||
cx.tab().selected_or_hovered().cloned().collect()
|
||||
};
|
||||
|
||||
if opt.targets.is_empty() {
|
||||
if form.targets.is_empty() {
|
||||
succ!();
|
||||
} else if opt.force {
|
||||
return act!(mgr:remove_do, cx, opt);
|
||||
} else if form.force {
|
||||
return act!(mgr:remove_do, cx, form);
|
||||
}
|
||||
|
||||
let confirm = ConfirmProxy::show(if opt.permanently {
|
||||
ConfirmCfg::delete(&opt.targets)
|
||||
let confirm = ConfirmProxy::show(if form.permanently {
|
||||
ConfirmCfg::delete(&form.targets)
|
||||
} else {
|
||||
ConfirmCfg::trash(&opt.targets)
|
||||
ConfirmCfg::trash(&form.targets)
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
if confirm.await {
|
||||
MgrProxy::remove_do(opt.targets, opt.permanently);
|
||||
MgrProxy::remove_do(form.targets, form.permanently);
|
||||
}
|
||||
});
|
||||
succ!();
|
||||
|
|
@ -52,17 +52,17 @@ impl Actor for RemoveDo {
|
|||
|
||||
const NAME: &str = "remove_do";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let mgr = &mut cx.mgr;
|
||||
|
||||
mgr.tabs.iter_mut().for_each(|t| {
|
||||
t.selected.remove_many(&opt.targets);
|
||||
t.selected.remove_many(&form.targets);
|
||||
});
|
||||
|
||||
mgr.yanked.remove_many(&opt.targets);
|
||||
mgr.yanked.remove_many(&form.targets);
|
||||
mgr.yanked.catchup_revision(false);
|
||||
|
||||
cx.tasks.file_remove(opt.targets, opt.permanently);
|
||||
cx.tasks.file_remove(form.targets, form.permanently);
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,17 @@ impl Actor for Rename {
|
|||
|
||||
const NAME: &str = "rename";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
if !opt.hovered && !cx.tab().selected.is_empty() {
|
||||
if !form.hovered && !cx.tab().selected.is_empty() {
|
||||
return act!(mgr:bulk_rename, cx);
|
||||
}
|
||||
|
||||
let Some(hovered) = cx.hovered() else { succ!() };
|
||||
|
||||
let name = Self::empty_url_part(&hovered.url, &opt.empty);
|
||||
let cursor = match opt.cursor.as_ref() {
|
||||
let name = Self::empty_url_part(&hovered.url, &form.empty);
|
||||
let cursor = match form.cursor.as_ref() {
|
||||
"start" => Some(0),
|
||||
"before_ext" => name
|
||||
.chars()
|
||||
|
|
@ -54,7 +54,7 @@ impl Actor for Rename {
|
|||
return;
|
||||
};
|
||||
|
||||
if opt.force || !maybe_exists(&new).await || provider::must_identical(&old, &new).await {
|
||||
if form.force || !maybe_exists(&new).await || provider::must_identical(&old, &new).await {
|
||||
Self::r#do(tab, old, new).await.ok();
|
||||
} else if ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await {
|
||||
Self::r#do(tab, old, new).await.ok();
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ impl Actor for Reveal {
|
|||
|
||||
const NAME: &str = "reveal";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
let Some((parent, child)) = opt.target.pair() else { succ!() };
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some((parent, child)) = form.target.pair() else { succ!() };
|
||||
|
||||
// Cd to the parent directory
|
||||
act!(mgr:cd, cx, (parent, opt.source))?;
|
||||
act!(mgr:cd, cx, (parent, form.source))?;
|
||||
|
||||
// Try to hover over the child file
|
||||
let tab = cx.tab_mut();
|
||||
|
|
@ -25,8 +25,8 @@ impl Actor for Reveal {
|
|||
|
||||
// If the child is not hovered, which means it doesn't exist,
|
||||
// create a dummy file
|
||||
if !opt.no_dummy && tab.hovered().is_none_or(|f| child != f.urn()) {
|
||||
let op = FilesOp::Creating(parent.into(), vec![File::from_dummy(&opt.target, None)]);
|
||||
if !form.no_dummy && tab.hovered().is_none_or(|f| child != f.urn()) {
|
||||
let op = FilesOp::Creating(parent.into(), vec![File::from_dummy(&form.target, None)]);
|
||||
tab.current.update_pub(tab.id, op);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ impl Actor for Seek {
|
|||
|
||||
const NAME: &str = "seek";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let Some(hovered) = cx.hovered() else {
|
||||
succ!(cx.tab_mut().preview.reset());
|
||||
};
|
||||
|
|
@ -28,7 +28,7 @@ impl Actor for Seek {
|
|||
succ!(cx.tab_mut().preview.reset());
|
||||
};
|
||||
|
||||
let job = SeekJob { file: hovered.clone(), units: opt.units };
|
||||
let job = SeekJob { file: hovered.clone(), units: form.units };
|
||||
let opt = PluginOpt::new_callback(&*previewer.run.name, move |_, plugin| {
|
||||
plugin.call_method("seek", job)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,33 +18,33 @@ impl Actor for Shell {
|
|||
|
||||
const NAME: &str = "shell";
|
||||
|
||||
fn act(cx: &mut Ctx, mut opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, mut form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
let cwd = opt.cwd.take().unwrap_or(cx.cwd().into()).into_owned();
|
||||
let cwd = form.cwd.take().unwrap_or(cx.cwd().into()).into_owned();
|
||||
let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect();
|
||||
|
||||
let input = opt.interactive.then(|| {
|
||||
InputProxy::show(InputCfg::shell(opt.block).with_value(&*opt.run).with_cursor(opt.cursor))
|
||||
let input = form.interactive.then(|| {
|
||||
InputProxy::show(InputCfg::shell(form.block).with_value(&*form.run).with_cursor(form.cursor))
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Some(mut rx) = input {
|
||||
match rx.recv().await {
|
||||
Some(InputEvent::Submit(e)) => opt.run = Cow::Owned(e),
|
||||
Some(InputEvent::Submit(e)) => form.run = Cow::Owned(e),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
if opt.run.is_empty() {
|
||||
if form.run.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
TasksProxy::open_shell_compat(ProcessOpt {
|
||||
cwd: cwd.into(),
|
||||
cmd: opt.run.to_string().into(),
|
||||
cmd: form.run.to_string().into(),
|
||||
args: selected,
|
||||
block: opt.block,
|
||||
orphan: opt.orphan,
|
||||
block: form.block,
|
||||
orphan: form.orphan,
|
||||
spread: true,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ impl Actor for Sort {
|
|||
|
||||
const NAME: &str = "sort";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let pref = &mut cx.tab_mut().pref;
|
||||
pref.sort_by = opt.by.unwrap_or(pref.sort_by);
|
||||
pref.sort_reverse = opt.reverse.unwrap_or(pref.sort_reverse);
|
||||
pref.sort_dir_first = opt.dir_first.unwrap_or(pref.sort_dir_first);
|
||||
pref.sort_sensitive = opt.sensitive.unwrap_or(pref.sort_sensitive);
|
||||
pref.sort_translit = opt.translit.unwrap_or(pref.sort_translit);
|
||||
pref.sort_fallback = opt.fallback.unwrap_or(pref.sort_fallback);
|
||||
pref.sort_by = form.by.unwrap_or(pref.sort_by);
|
||||
pref.sort_reverse = form.reverse.unwrap_or(pref.sort_reverse);
|
||||
pref.sort_dir_first = form.dir_first.unwrap_or(pref.sort_dir_first);
|
||||
pref.sort_sensitive = form.sensitive.unwrap_or(pref.sort_sensitive);
|
||||
pref.sort_translit = form.translit.unwrap_or(pref.sort_translit);
|
||||
pref.sort_fallback = form.fallback.unwrap_or(pref.sort_fallback);
|
||||
|
||||
let sorter = FilesSorter::from(&*pref);
|
||||
let hovered = cx.hovered().map(|f| f.urn().to_owned());
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl Actor for Spot {
|
|||
|
||||
const NAME: &str = "spot";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
let Some(hovered) = cx.hovered().cloned() else { succ!() };
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ impl Actor for Spot {
|
|||
// self.active_mut().spot.reset();
|
||||
// }
|
||||
|
||||
if let Some(skip) = opt.skip {
|
||||
if let Some(skip) = form.skip {
|
||||
cx.tab_mut().spot.skip = skip;
|
||||
} else if !cx.tab().spot.same_url(&hovered.url) {
|
||||
cx.tab_mut().spot.skip = 0;
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ impl Actor for Stash {
|
|||
|
||||
const NAME: &str = "stash";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
if opt.target.is_absolute() && opt.target.is_internal() {
|
||||
cx.tab_mut().backstack.push(opt.target.as_url());
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.target.is_absolute() && form.target.is_internal() {
|
||||
cx.tab_mut().backstack.push(form.target.as_url());
|
||||
}
|
||||
|
||||
succ!()
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _opt: &Self::Form) -> Option<SparkKind> {
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
match cx.source() {
|
||||
Source::Ind => Some(SparkKind::IndStash),
|
||||
Source::Relay => Some(SparkKind::RelayStash),
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ impl Actor for TabClose {
|
|||
|
||||
const NAME: &str = "tab_close";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let len = cx.tabs().len();
|
||||
if len < 2 || opt.idx >= len {
|
||||
if len < 2 || form.idx >= len {
|
||||
succ!();
|
||||
}
|
||||
|
||||
let tabs = cx.tabs_mut();
|
||||
tabs.remove(opt.idx).shutdown();
|
||||
tabs.remove(form.idx).shutdown();
|
||||
|
||||
if opt.idx > tabs.cursor {
|
||||
if form.idx > tabs.cursor {
|
||||
tabs.set_idx(tabs.cursor);
|
||||
} else {
|
||||
tabs.set_idx(usize::min(tabs.cursor + 1, tabs.len() - 1));
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ impl Actor for TabCreate {
|
|||
|
||||
const NAME: &str = "tab_create";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if cx.tabs().len() >= MAX_TABS {
|
||||
succ!(NotifyProxy::push_warn(
|
||||
"Too many tabs",
|
||||
|
|
@ -25,7 +25,7 @@ impl Actor for TabCreate {
|
|||
}
|
||||
|
||||
let mut tab = Tab::default();
|
||||
let (cd, url) = if let Some(wd) = opt.url {
|
||||
let (cd, url) = if let Some(wd) = form.url {
|
||||
(true, wd.into_owned())
|
||||
} else if let Some(h) = cx.hovered() {
|
||||
tab.pref = cx.tab().pref.clone();
|
||||
|
|
|
|||
|
|
@ -17,18 +17,18 @@ impl Actor for TabRename {
|
|||
|
||||
const NAME: &str = "tab_rename";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab().id;
|
||||
let pref = &mut cx.tab_mut().pref;
|
||||
|
||||
if !opt.interactive {
|
||||
pref.name = opt.name.unwrap_or_default().into_owned();
|
||||
if !form.interactive {
|
||||
pref.name = form.name.unwrap_or_default().into_owned();
|
||||
act!(app:title, cx).ok();
|
||||
succ!(render!());
|
||||
}
|
||||
|
||||
let mut input = InputProxy::show(
|
||||
InputCfg::tab_rename().with_value(opt.name.unwrap_or(Cow::Borrowed(&pref.name))),
|
||||
InputCfg::tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name))),
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
if let Some(InputEvent::Submit(name)) = input.recv().await {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ impl Actor for TabSwap {
|
|||
|
||||
const NAME: &str = "tab_swap";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tabs = cx.tabs_mut();
|
||||
|
||||
let new = opt.step.add(tabs.cursor, tabs.len(), 0);
|
||||
let new = form.step.add(tabs.cursor, tabs.len(), 0);
|
||||
if new == tabs.cursor {
|
||||
succ!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ impl Actor for TabSwitch {
|
|||
|
||||
const NAME: &str = "tab_switch";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tabs = cx.tabs_mut();
|
||||
let idx = if opt.relative {
|
||||
opt.step.saturating_add_unsigned(tabs.cursor).rem_euclid(tabs.len() as _) as _
|
||||
let idx = if form.relative {
|
||||
form.step.saturating_add_unsigned(tabs.cursor).rem_euclid(tabs.len() as _) as _
|
||||
} else {
|
||||
opt.step as usize
|
||||
form.step as usize
|
||||
};
|
||||
|
||||
if idx == tabs.cursor || idx >= tabs.len() {
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ impl Actor for Toggle {
|
|||
|
||||
const NAME: &str = "toggle";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
let Some(url) = opt.url.or(tab.current.hovered().map(|h| UrlCow::from(&h.url))) else {
|
||||
let Some(url) = form.url.or(tab.current.hovered().map(|h| UrlCow::from(&h.url))) else {
|
||||
succ!();
|
||||
};
|
||||
|
||||
let b = match opt.state {
|
||||
let b = match form.state {
|
||||
Some(true) => render_and!(tab.selected.add(&url)),
|
||||
Some(false) => render_and!(tab.selected.remove(&url)) | true,
|
||||
None => render_and!(tab.selected.remove(&url) || tab.selected.add(&url)),
|
||||
|
|
|
|||
|
|
@ -13,18 +13,18 @@ impl Actor for ToggleAll {
|
|||
|
||||
const NAME: &str = "toggle_all";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
use either::Either::*;
|
||||
let tab = cx.tab_mut();
|
||||
|
||||
let it = tab.current.files.iter().map(|f| &f.url);
|
||||
let either = match opt.state {
|
||||
Some(true) if opt.urls.is_empty() => Left((vec![], it.collect())),
|
||||
Some(true) => Right((vec![], opt.urls)),
|
||||
Some(false) if opt.urls.is_empty() => Left((it.collect(), vec![])),
|
||||
Some(false) => Right((opt.urls, vec![])),
|
||||
None if opt.urls.is_empty() => Left(it.partition(|&u| tab.selected.contains(u))),
|
||||
None => Right(opt.urls.into_iter().partition(|u| tab.selected.contains(u))),
|
||||
let either = match form.state {
|
||||
Some(true) if form.urls.is_empty() => Left((vec![], it.collect())),
|
||||
Some(true) => Right((vec![], form.urls)),
|
||||
Some(false) if form.urls.is_empty() => Left((it.collect(), vec![])),
|
||||
Some(false) => Right((form.urls, vec![])),
|
||||
None if form.urls.is_empty() => Left(it.partition(|&u| tab.selected.contains(u))),
|
||||
None => Right(form.urls.into_iter().partition(|u| tab.selected.contains(u))),
|
||||
};
|
||||
|
||||
let warn = match either {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ impl Actor for UpdateFiles {
|
|||
|
||||
const NAME: &str = "update_files";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let revision = cx.current().files.revision;
|
||||
let linked: Vec<_> = LINKED.read().from_dir(opt.op.cwd()).map(|u| opt.op.chdir(u)).collect();
|
||||
let linked: Vec<_> = LINKED.read().from_dir(form.op.cwd()).map(|u| form.op.chdir(u)).collect();
|
||||
|
||||
for op in [opt.op].into_iter().chain(linked) {
|
||||
for op in [form.op].into_iter().chain(linked) {
|
||||
cx.mgr.yanked.apply_op(&op);
|
||||
Self::update_tab(cx, op).ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ impl Actor for UpdateMimes {
|
|||
|
||||
const NAME: &str = "update_mimes";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let linked = LINKED.read();
|
||||
let updates = opt
|
||||
let updates = form
|
||||
.updates
|
||||
.into_iter()
|
||||
.filter(|(url, mime)| cx.mgr.mimetype.get(url) != Some(mime))
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ impl Actor for UpdatePaged {
|
|||
|
||||
const NAME: &str = "update_paged";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
if opt.only_if.is_some_and(|u| u != *cx.cwd()) {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.only_if.is_some_and(|u| u != *cx.cwd()) {
|
||||
succ!();
|
||||
}
|
||||
|
||||
let targets = cx.current().paginate(opt.page.unwrap_or(cx.current().page));
|
||||
let targets = cx.current().paginate(form.page.unwrap_or(cx.current().page));
|
||||
if !targets.is_empty() {
|
||||
cx.tasks.fetch_paged(targets, &cx.mgr.mimetype);
|
||||
cx.tasks.preload_paged(targets, &cx.mgr.mimetype);
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ impl Actor for UpdateYanked {
|
|||
|
||||
const NAME: &str = "update_yanked";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
if opt.urls.is_empty() && cx.mgr.yanked.is_empty() {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.urls.is_empty() && cx.mgr.yanked.is_empty() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
cx.mgr.yanked = Yanked::new(opt.cut, opt.0.urls.into_owned());
|
||||
cx.mgr.yanked = Yanked::new(form.cut, form.0.urls.into_owned());
|
||||
succ!(render!());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ impl Actor for Upload {
|
|||
|
||||
const NAME: &str = "upload";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
for url in opt.urls {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
for url in form.urls {
|
||||
cx.tasks.scheduler.file_upload(url.into_owned());
|
||||
}
|
||||
succ!();
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ impl Actor for VisualMode {
|
|||
|
||||
const NAME: &str = "visual_mode";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tab = cx.tab_mut();
|
||||
|
||||
let idx = tab.current.cursor;
|
||||
if opt.unset {
|
||||
if form.unset {
|
||||
tab.mode = Mode::Unset(idx, BTreeSet::from([idx]));
|
||||
} else {
|
||||
tab.mode = Mode::Select(idx, BTreeSet::from([idx]));
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ impl Actor for Yank {
|
|||
|
||||
const NAME: &str = "yank";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:escape_visual, cx)?;
|
||||
|
||||
cx.mgr.yanked =
|
||||
Yanked::new(opt.cut, cx.tab().selected_or_hovered().cloned().map(UrlBufCov).collect());
|
||||
Yanked::new(form.cut, cx.tab().selected_or_hovered().cloned().map(UrlBufCov).collect());
|
||||
render!(cx.mgr.yanked.catchup_revision(true));
|
||||
|
||||
act!(mgr:escape_select, cx)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ impl Actor for Tick {
|
|||
|
||||
const NAME: &str = "tick";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
cx.notify.ticker.take().map(|h| h.abort());
|
||||
|
||||
let Dimension { rows, columns, .. } = Dimension::available();
|
||||
|
|
@ -35,7 +35,7 @@ impl Actor for Tick {
|
|||
} else if m.percent < 100 {
|
||||
m.percent += 20;
|
||||
} else {
|
||||
m.timeout = m.timeout.saturating_sub(opt.interval);
|
||||
m.timeout = m.timeout.saturating_sub(form.interval);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.pick.scroll(opt.step)));
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
succ!(render!(cx.pick.scroll(form.step)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ impl Actor for Close {
|
|||
|
||||
const NAME: &str = "close";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let pick = &mut cx.pick;
|
||||
if let Some(cb) = pick.callback.take() {
|
||||
_ = cb.send(if opt.submit { Some(pick.cursor) } else { None });
|
||||
_ = cb.send(if form.submit { Some(pick.cursor) } else { None });
|
||||
}
|
||||
|
||||
pick.cursor = 0;
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@ impl Actor for Show {
|
|||
|
||||
const NAME: &str = "show";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(pick:close, cx)?;
|
||||
|
||||
let pick = &mut cx.pick;
|
||||
pick.title = opt.cfg.title;
|
||||
pick.items = opt.cfg.items;
|
||||
pick.position = opt.cfg.position;
|
||||
pick.title = form.cfg.title;
|
||||
pick.items = form.cfg.items;
|
||||
pick.position = form.cfg.position;
|
||||
|
||||
pick.callback = Some(opt.tx);
|
||||
pick.callback = Some(form.tx);
|
||||
pick.visible = true;
|
||||
succ!(render!());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let spot = &mut cx.tab_mut().spot;
|
||||
let Some(lock) = &mut spot.lock else { succ!() };
|
||||
|
||||
let new = opt.step.add(spot.skip, lock.len().unwrap_or(u16::MAX as _), 0);
|
||||
let new = form.step.add(spot.skip, lock.len().unwrap_or(u16::MAX as _), 0);
|
||||
let Some(old) = lock.selected() else {
|
||||
return act!(mgr:spot, cx, new);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ impl Actor for Copy {
|
|||
|
||||
const NAME: &str = "copy";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let spot = &cx.tab().spot;
|
||||
let Some(lock) = &spot.lock else { succ!() };
|
||||
let Some(table) = lock.table() else { succ!() };
|
||||
|
||||
let mut s = String::new();
|
||||
match opt.r#type.as_ref() {
|
||||
match form.r#type.as_ref() {
|
||||
"cell" => {
|
||||
let Some(cell) = table.selected_cell() else { succ!() };
|
||||
s = cell.to_string();
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ impl Actor for Swipe {
|
|||
|
||||
const NAME: &str = "swipe";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
act!(mgr:arrow, cx, opt)?;
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
act!(mgr:arrow, cx, form)?;
|
||||
act!(mgr:spot, cx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ impl Actor for Arrow {
|
|||
|
||||
const NAME: &str = "arrow";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
let tasks = &mut cx.tasks;
|
||||
|
||||
let old = tasks.cursor;
|
||||
tasks.cursor = opt.step.add(tasks.cursor, tasks.snaps.len(), Tasks::limit());
|
||||
tasks.cursor = form.step.add(tasks.cursor, tasks.snaps.len(), Tasks::limit());
|
||||
|
||||
succ!(render!(tasks.cursor != old));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use yazi_macro::succ;
|
||||
use yazi_macro::{act, succ};
|
||||
use yazi_parser::tasks::UpdateSucceedForm;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -12,23 +12,21 @@ impl Actor for UpdateSucceed {
|
|||
|
||||
const NAME: &str = "update_succeed";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
|
||||
if opt.urls.is_empty() {
|
||||
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
|
||||
if form.urls.is_empty() {
|
||||
succ!();
|
||||
}
|
||||
|
||||
// FIXME: todo!();
|
||||
// if opt.track
|
||||
// && opt.batch == cx.tasks.scheduler.batch.current()
|
||||
// && let Some((parent, urn)) = opt.urls[0].pair()
|
||||
// && parent == *cx.cwd()
|
||||
// {
|
||||
// cx.tasks.scheduler.batch.next();
|
||||
// cx.current_mut().trace = Some(urn.into());
|
||||
// act!(mgr:hover, cx)?;
|
||||
// }
|
||||
if form.track
|
||||
&& form.id == cx.tasks.scheduler.behavior.first_id()
|
||||
&& let Some((parent, urn)) = form.urls[0].pair()
|
||||
&& parent == *cx.cwd()
|
||||
{
|
||||
cx.current_mut().trace = Some(urn.into());
|
||||
act!(mgr:hover, cx)?;
|
||||
}
|
||||
|
||||
cx.mgr.watcher.report(opt.urls);
|
||||
cx.mgr.watcher.report(form.urls);
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Actor for Activate {
|
|||
succ!(render!());
|
||||
}
|
||||
|
||||
fn hook(cx: &Ctx, _opt: &Self::Form) -> Option<SparkKind> {
|
||||
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
|
||||
match cx.source() {
|
||||
Source::Unknown => Some(SparkKind::IndWhichActivate),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ impl Folder {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
let mut deleted = vec![];
|
||||
match op {
|
||||
FilesOp::Full(_, files, _) => self.files.update_full(files),
|
||||
FilesOp::Part(_, files, ticket) => self.files.update_part(files, ticket),
|
||||
|
|
@ -71,16 +72,13 @@ impl Folder {
|
|||
FilesOp::IOErr(..) => self.files.update_ioerr(),
|
||||
|
||||
FilesOp::Creating(_, files) => self.files.update_creating(files),
|
||||
FilesOp::Deleting(_, urns) => {
|
||||
let deleted = self.files.update_deleting(urns);
|
||||
let delta = deleted.into_iter().filter(|&i| i < self.cursor).count() as isize;
|
||||
self.arrow(-delta);
|
||||
}
|
||||
FilesOp::Deleting(_, urns) => deleted = self.files.update_deleting(urns),
|
||||
FilesOp::Updating(_, files) => _ = self.files.update_updating(files),
|
||||
FilesOp::Upserting(_, files) => self.files.update_upserting(files),
|
||||
}
|
||||
};
|
||||
|
||||
self.trace.take_if(|_| self.files.is_empty() && !self.stage.is_loading());
|
||||
self.arrow(-(deleted.into_iter().filter(|&i| i < self.cursor).count() as isize));
|
||||
self.repos(None);
|
||||
|
||||
(&stage, revision) != (&self.stage, self.files.revision)
|
||||
|
|
@ -101,9 +99,7 @@ impl Folder {
|
|||
self.scroll(step)
|
||||
};
|
||||
|
||||
self.trace = self.hovered().filter(|_| b).map(|h| h.urn().into()).or(self.trace.take());
|
||||
b |= self.squeeze_offset();
|
||||
|
||||
self.sync_page(false);
|
||||
b
|
||||
}
|
||||
|
|
@ -114,19 +110,28 @@ impl Folder {
|
|||
}
|
||||
|
||||
let new = self.files.position(urn).unwrap_or(self.cursor) as isize;
|
||||
self.arrow(new - self.cursor as isize)
|
||||
let b = self.arrow(new - self.cursor as isize);
|
||||
|
||||
self.retrace();
|
||||
b
|
||||
}
|
||||
|
||||
pub fn repos(&mut self, urn: Option<PathDyn>) -> bool {
|
||||
if let Some(u) = urn {
|
||||
self.hover(u)
|
||||
} else if let Some(u) = &self.trace {
|
||||
self.hover(u.clone().as_path())
|
||||
} else if let Some(u) = self.trace.take() {
|
||||
let b = self.hover(u.as_path());
|
||||
self.trace = Some(u);
|
||||
b
|
||||
} else {
|
||||
self.arrow(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retrace(&mut self) {
|
||||
self.trace = self.hovered().map(|h| h.urn().into()).or(self.trace.take());
|
||||
}
|
||||
|
||||
pub fn sync_page(&mut self, force: bool) {
|
||||
let limit = LAYOUT.get().folder_limit();
|
||||
if limit == 0 {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use crate::mgr::Yanked;
|
|||
|
||||
impl Tasks {
|
||||
pub fn file_cut(&self, src: &Yanked, dest: &UrlBuf, force: bool) {
|
||||
self.scheduler.behavior.reset();
|
||||
|
||||
for u in src.iter() {
|
||||
let Some(Ok(to)) = u.name().map(|n| dest.try_join(n)) else {
|
||||
debug!("file_cut: cannot join {u:?} with {dest:?}");
|
||||
|
|
@ -21,6 +23,8 @@ impl Tasks {
|
|||
}
|
||||
|
||||
pub fn file_copy(&self, src: &Yanked, dest: &UrlBuf, force: bool, follow: bool) {
|
||||
self.scheduler.behavior.reset();
|
||||
|
||||
for u in src.iter() {
|
||||
let Some(Ok(to)) = u.name().map(|n| dest.try_join(n)) else {
|
||||
debug!("file_copy: cannot join {u:?} with {dest:?}");
|
||||
|
|
@ -35,6 +39,8 @@ impl Tasks {
|
|||
}
|
||||
|
||||
pub fn file_link(&self, src: &IndexSet<UrlBufCov>, dest: &UrlBuf, relative: bool, force: bool) {
|
||||
self.scheduler.behavior.reset();
|
||||
|
||||
for u in src {
|
||||
let Some(Ok(to)) = u.name().map(|n| dest.try_join(n)) else {
|
||||
debug!("file_link: cannot join {u:?} with {dest:?}");
|
||||
|
|
@ -49,6 +55,8 @@ impl Tasks {
|
|||
}
|
||||
|
||||
pub fn file_hardlink(&self, src: &IndexSet<UrlBufCov>, dest: &UrlBuf, force: bool, follow: bool) {
|
||||
self.scheduler.behavior.reset();
|
||||
|
||||
for u in src {
|
||||
let Some(Ok(to)) = u.name().map(|n| dest.try_join(n)) else {
|
||||
debug!("file_hardlink: cannot join {u:?} with {dest:?}");
|
||||
|
|
|
|||
|
|
@ -53,12 +53,12 @@ impl<'a> Executor<'a> {
|
|||
"resize" => act!(app:resize, cx, crate::Root::reflow as fn(_) -> _),
|
||||
"resume" => act!(app:resume, cx, yazi_parser::app::ResumeForm {
|
||||
tx: self.app.signals.tx.clone(),
|
||||
token: action.take_any("token").context("Invalid 'token' in ResumeForm")?,
|
||||
reflow: crate::Root::reflow,
|
||||
replier: action.take_replier().context("Missing replier in ResumeForm")?,
|
||||
}),
|
||||
"stop" => act!(app:stop, cx, yazi_parser::app::StopForm {
|
||||
tx: self.app.signals.tx.clone(),
|
||||
token: action.take_any("token").context("Invalid 'token' in StopForm")?,
|
||||
replier: action.take_replier().context("Missing replier in StopForm")?,
|
||||
}),
|
||||
_ => succ!(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventK
|
|||
use futures::StreamExt;
|
||||
use tokio::{select, sync::mpsc};
|
||||
use yazi_config::YAZI;
|
||||
use yazi_shared::{CompletionToken, event::Event};
|
||||
use yazi_shared::event::{Event, Replier};
|
||||
|
||||
pub(super) struct Signals {
|
||||
pub(super) tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
||||
pub(super) tx: mpsc::UnboundedSender<(bool, Replier)>,
|
||||
}
|
||||
|
||||
impl Signals {
|
||||
|
|
@ -65,7 +65,7 @@ impl Signals {
|
|||
}
|
||||
}
|
||||
|
||||
fn spawn(mut rx: mpsc::UnboundedReceiver<(bool, CompletionToken)>) -> Result<()> {
|
||||
fn spawn(mut rx: mpsc::UnboundedReceiver<(bool, Replier)>) -> Result<()> {
|
||||
#[cfg(unix)]
|
||||
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP};
|
||||
|
||||
|
|
@ -90,9 +90,9 @@ impl Signals {
|
|||
if let Some(t) = &mut term {
|
||||
select! {
|
||||
biased;
|
||||
Some((state, token)) = rx.recv() => {
|
||||
Some((state, replier)) = rx.recv() => {
|
||||
term = term.filter(|_| state);
|
||||
token.complete(true);
|
||||
replier.send(Ok(().into())).ok();
|
||||
},
|
||||
Some(n) = sys.next() => if !Self::handle_sys(n) { return },
|
||||
Some(Ok(e)) = t.next() => Self::handle_term(e)
|
||||
|
|
@ -100,9 +100,9 @@ impl Signals {
|
|||
} else {
|
||||
select! {
|
||||
biased;
|
||||
Some((state, token)) = rx.recv() => {
|
||||
Some((state, replier)) = rx.recv() => {
|
||||
term = state.then(EventStream::new);
|
||||
token.complete(true);
|
||||
replier.send(Ok(().into())).ok();
|
||||
},
|
||||
Some(n) = sys.next() => if !Self::handle_sys(n) { return },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ yazi-shim = { path = "../yazi-shim", version = "26.2.2" }
|
|||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
arc-swap = "1.9.0"
|
||||
arc-swap = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
either = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
|
||||
use ratatui::layout::Rect;
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_shared::CompletionToken;
|
||||
use yazi_shared::event::Replier;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResumeForm {
|
||||
pub tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
||||
pub token: CompletionToken,
|
||||
pub reflow: fn(Rect) -> mlua::Result<Table>,
|
||||
pub tx: mpsc::UnboundedSender<(bool, Replier)>,
|
||||
pub reflow: fn(Rect) -> mlua::Result<Table>,
|
||||
pub replier: Replier,
|
||||
}
|
||||
|
||||
impl FromLua for ResumeForm {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_shared::CompletionToken;
|
||||
use yazi_shared::event::Replier;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StopForm {
|
||||
pub tx: mpsc::UnboundedSender<(bool, CompletionToken)>,
|
||||
pub token: CompletionToken,
|
||||
pub tx: mpsc::UnboundedSender<(bool, Replier)>,
|
||||
pub replier: Replier,
|
||||
}
|
||||
|
||||
impl FromLua for StopForm {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::bail;
|
||||
use anyhow::anyhow;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use yazi_core::cmp::CmpOpt;
|
||||
use yazi_shared::event::ActionCow;
|
||||
|
|
@ -16,11 +16,7 @@ impl TryFrom<ActionCow> for ShowForm {
|
|||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut a: ActionCow) -> Result<Self, Self::Error> {
|
||||
if let Some(opt) = a.take_any2("opt") {
|
||||
opt
|
||||
} else {
|
||||
bail!("Invalid 'opt' in ShowForm");
|
||||
}
|
||||
Ok(Self { opt: a.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in ShowForm"))? })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ impl TryFrom<ActionCow> for StashForm {
|
|||
}
|
||||
|
||||
impl From<CdForm> for StashForm {
|
||||
fn from(opt: CdForm) -> Self { Self { target: opt.target, source: opt.source } }
|
||||
fn from(form: CdForm) -> Self { Self { target: form.target, source: form.source } }
|
||||
}
|
||||
|
||||
impl FromLua for StashForm {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
use anyhow::anyhow;
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||
use tokio::sync::mpsc;
|
||||
use yazi_scheduler::process::ProcessOpt;
|
||||
use yazi_shared::{data::Data, event::ActionCow};
|
||||
use yazi_shared::event::{ActionCow, Replier};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ProcessOpenForm {
|
||||
pub opt: ProcessOpt,
|
||||
pub replier: Option<mpsc::UnboundedSender<anyhow::Result<Data>>>,
|
||||
pub replier: Option<Replier>,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for ProcessOpenForm {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ pub(super) fn lines(lua: &Lua) -> mlua::Result<Value> {
|
|||
parsed = s.to_text().into_lua_err()?.lines;
|
||||
LineIter::parsed(&parsed, tab_size)
|
||||
} else {
|
||||
LineIter::source(&s, tab_size)
|
||||
LineIter::source(s, tab_size)
|
||||
};
|
||||
|
||||
if let Some(wrap) = *opts.raw_get::<Wrap>("wrap")? {
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ pub fn slim_lua(lua: &Lua) -> mlua::Result<()> {
|
|||
globals.raw_set("ya", crate::utils::compose(true))?;
|
||||
globals.raw_set("fs", crate::fs::compose())?;
|
||||
globals.raw_set("rt", crate::runtime::compose())?;
|
||||
globals.raw_set("th", crate::theme::compose().into_lua(&lua)?)?;
|
||||
globals.raw_set("th", crate::theme::compose().into_lua(lua)?)?;
|
||||
|
||||
yazi_binding::Cha::install(&lua)?;
|
||||
yazi_binding::File::install(&lua)?;
|
||||
yazi_binding::Url::install(&lua)?;
|
||||
yazi_binding::Path::install(&lua)?;
|
||||
yazi_binding::Cha::install(lua)?;
|
||||
yazi_binding::File::install(lua)?;
|
||||
yazi_binding::Url::install(lua)?;
|
||||
yazi_binding::Path::install(lua)?;
|
||||
|
||||
yazi_binding::Error::install(&lua)?;
|
||||
yazi_binding::process::install(&lua)?;
|
||||
yazi_runner::loader::install(&lua)?;
|
||||
yazi_binding::Error::install(lua)?;
|
||||
yazi_binding::process::install(lua)?;
|
||||
yazi_runner::loader::install(lua)?;
|
||||
|
||||
// Addons
|
||||
lua.load(preset!("ya")).set_name("ya.lua").exec()?;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ impl IntoLua for FetchJob {
|
|||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("args", Sendable::args_to_table_ref(&lua, &self.action.args)?.into_lua(&lua)?),
|
||||
("files", lua.create_sequence_from(self.files.into_iter().map(File::new))?.into_lua(&lua)?),
|
||||
("args", Sendable::args_to_table_ref(lua, &self.action.args)?.into_lua(lua)?),
|
||||
("files", lua.create_sequence_from(self.files.into_iter().map(File::new))?.into_lua(lua)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ impl IntoLua for PeekJob {
|
|||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?),
|
||||
("args", Sendable::args_to_table_ref(&lua, &self.action.args)?.into_lua(&lua)?),
|
||||
("file", File::new(self.file).into_lua(&lua)?),
|
||||
("mime", self.mime.into_lua(&lua)?),
|
||||
("skip", self.skip.into_lua(&lua)?),
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
|
||||
("args", Sendable::args_to_table_ref(lua, &self.action.args)?.into_lua(lua)?),
|
||||
("file", File::new(self.file).into_lua(lua)?),
|
||||
("mime", self.mime.into_lua(lua)?),
|
||||
("skip", self.skip.into_lua(lua)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
|
|
@ -37,9 +37,9 @@ impl IntoLua for SeekJob {
|
|||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?),
|
||||
("file", File::new(self.file).into_lua(&lua)?),
|
||||
("units", self.units.into_lua(&lua)?),
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
|
||||
("file", File::new(self.file).into_lua(lua)?),
|
||||
("units", self.units.into_lua(lua)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
|
|
|
|||
19
yazi-scheduler/src/behavior.rs
Normal file
19
yazi-scheduler/src/behavior.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use yazi_shared::Id;
|
||||
|
||||
pub struct Behavior {
|
||||
first_id: AtomicU64,
|
||||
}
|
||||
|
||||
impl Behavior {
|
||||
pub(super) fn new() -> Self { Self { first_id: AtomicU64::new(0) } }
|
||||
|
||||
pub(super) fn update(&self, id: Id) {
|
||||
self.first_id.compare_exchange(0, id.get(), Ordering::Relaxed, Ordering::Relaxed).ok();
|
||||
}
|
||||
|
||||
pub fn reset(&self) { self.first_id.store(0, Ordering::Relaxed); }
|
||||
|
||||
pub fn first_id(&self) -> Id { self.first_id.load(Ordering::Relaxed).into() }
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ pub(crate) struct FetchIn {
|
|||
}
|
||||
|
||||
impl From<FetchIn> for FetchJob {
|
||||
fn from(value: FetchIn) -> Self { FetchJob { action: &value.plugin.run, files: value.targets } }
|
||||
fn from(value: FetchIn) -> Self { Self { action: &value.plugin.run, files: value.targets } }
|
||||
}
|
||||
|
||||
impl FetchIn {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl Hook {
|
|||
}
|
||||
|
||||
let result = ok_or_not_found(provider::remove_dir_clean(&task.from).await);
|
||||
TasksProxy::update_succeed(task.id, [&task.to, &task.from], false);
|
||||
TasksProxy::update_succeed(task.id, [&task.to, &task.from], true);
|
||||
Pump::push_move(task.from, task.to);
|
||||
|
||||
self.ops.out(task.id, FileOutCut::Clean(result));
|
||||
|
|
@ -38,7 +38,7 @@ impl Hook {
|
|||
|
||||
pub(crate) async fn copy(&self, task: HookInOutCopy) {
|
||||
if self.ongoing.lock().intact(task.id) {
|
||||
TasksProxy::update_succeed(task.id, [&task.to], false);
|
||||
TasksProxy::update_succeed(task.id, [&task.to], true);
|
||||
Pump::push_duplicate(task.from, task.to);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ mod macros;
|
|||
|
||||
yazi_macro::mod_pub!(fetch file hook plugin preload process size);
|
||||
|
||||
yazi_macro::mod_flat!(ongoing op out progress proxy scheduler snap summary task worker);
|
||||
yazi_macro::mod_flat!(behavior ongoing op out progress proxy scheduler snap summary task worker);
|
||||
|
||||
const LOW: u8 = yazi_config::Priority::Low as u8;
|
||||
const NORMAL: u8 = yazi_config::Priority::Normal as u8;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
use tokio::sync::mpsc;
|
||||
use yazi_macro::{emit, relay};
|
||||
use yazi_shared::{CompletionToken, Id, SStr, url::UrlBuf};
|
||||
use yazi_shared::{Id, SStr, url::UrlBuf};
|
||||
|
||||
pub struct AppProxy;
|
||||
|
||||
impl AppProxy {
|
||||
// FIXME: use replier instead of token
|
||||
pub async fn stop() {
|
||||
let token = CompletionToken::default();
|
||||
emit!(Call(relay!(app:stop).with_any("token", token.clone())));
|
||||
token.future().await;
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
emit!(Call(relay!(app:stop).with_replier(tx)));
|
||||
rx.recv().await;
|
||||
}
|
||||
|
||||
// FIXME: use replier instead of token
|
||||
pub async fn resume() {
|
||||
let token = CompletionToken::default();
|
||||
emit!(Call(relay!(app:resume).with_any("token", token.clone())));
|
||||
token.future().await;
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
emit!(Call(relay!(app:resume).with_replier(tx)));
|
||||
rx.recv().await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
use std::{ops::Deref, sync::{Arc, atomic::{AtomicU64, Ordering}}, time::Duration};
|
||||
use std::{ops::Deref, sync::Arc, time::Duration};
|
||||
|
||||
use tokio::task::JoinHandle;
|
||||
use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
|
||||
use yazi_runner::plugin::PluginOpt;
|
||||
use yazi_shared::{CompletionToken, Id, Throttle, url::{UrlBuf, UrlLike}};
|
||||
|
||||
use crate::{HIGH, LOW, NORMAL, Worker, Task, TaskProg, fetch::{FetchIn, FetchProg}, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInTrash, HookInUpload}, plugin::{PluginInEntry, PluginProgEntry}, preload::{PreloadIn, PreloadProg}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOpt, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::{SizeIn, SizeProg}};
|
||||
use crate::{Behavior, HIGH, LOW, NORMAL, Task, TaskProg, Worker, fetch::{FetchIn, FetchProg}, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInTrash, HookInUpload}, plugin::{PluginInEntry, PluginProgEntry}, preload::{PreloadIn, PreloadProg}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOpt, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::{SizeIn, SizeProg}};
|
||||
|
||||
pub struct Scheduler {
|
||||
pub worker: Worker,
|
||||
pub user_action: AtomicU64,
|
||||
handles: Vec<JoinHandle<()>>,
|
||||
pub worker: Worker,
|
||||
pub behavior: Behavior,
|
||||
handles: Vec<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Deref for Scheduler {
|
||||
|
|
@ -22,7 +22,7 @@ impl Deref for Scheduler {
|
|||
impl Scheduler {
|
||||
pub fn serve() -> Self {
|
||||
let (worker, handles) = Worker::make();
|
||||
Self { worker, user_action: AtomicU64::new(0), handles }
|
||||
Self { worker, behavior: Behavior::new(), handles }
|
||||
}
|
||||
|
||||
fn add<T, R>(&self, name: String, map: impl FnOnce(&mut Task) -> R) -> R
|
||||
|
|
@ -30,14 +30,11 @@ impl Scheduler {
|
|||
T: Into<TaskProg> + Default,
|
||||
{
|
||||
let prog = T::default().into();
|
||||
let is_user = prog.is_user();
|
||||
|
||||
let mut ongoing = self.ongoing.lock();
|
||||
let task = ongoing.add(name, prog);
|
||||
|
||||
if is_user {
|
||||
self.user_action.store(task.id.get(), Ordering::Relaxed);
|
||||
}
|
||||
self.behavior.update(task.id);
|
||||
map(task)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ impl From<&Ongoing> for TaskSummary {
|
|||
let mut percent_count = 0;
|
||||
|
||||
for task in value.values() {
|
||||
let s: TaskSummary = task.prog.into();
|
||||
let s: Self = task.prog.into();
|
||||
if s.total == 0 && !task.prog.failed() {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,19 +12,19 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Nil => visitor.visit_unit(),
|
||||
Data::Boolean(b) => visitor.visit_bool(b),
|
||||
Data::Integer(i) => visitor.visit_i64(i),
|
||||
Data::Number(n) => visitor.visit_f64(n),
|
||||
Data::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
|
||||
Data::String(Cow::Owned(s)) => visitor.visit_string(s),
|
||||
Data::List(l) => visitor.visit_seq(SeqDeserializer { iter: l.into_iter() }),
|
||||
Data::Dict(d) => visitor.visit_map(MapDeserializer { iter: d.into_iter(), value: None }),
|
||||
Data::Id(i) => visitor.visit_u64(i.get()),
|
||||
Data::Url(u) => u.into_deserializer().deserialize_any(visitor),
|
||||
Data::Path(_) => Err(Error::custom("path not supported")),
|
||||
Data::Bytes(b) => BytesDeserializer(b.into()).deserialize_any(visitor),
|
||||
Data::Any(_) => Err(Error::custom("any not supported")),
|
||||
Self::Nil => visitor.visit_unit(),
|
||||
Self::Boolean(b) => visitor.visit_bool(b),
|
||||
Self::Integer(i) => visitor.visit_i64(i),
|
||||
Self::Number(n) => visitor.visit_f64(n),
|
||||
Self::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
|
||||
Self::String(Cow::Owned(s)) => visitor.visit_string(s),
|
||||
Self::List(l) => visitor.visit_seq(SeqDeserializer { iter: l.into_iter() }),
|
||||
Self::Dict(d) => visitor.visit_map(MapDeserializer { iter: d.into_iter(), value: None }),
|
||||
Self::Id(i) => visitor.visit_u64(i.get()),
|
||||
Self::Url(u) => u.into_deserializer().deserialize_any(visitor),
|
||||
Self::Path(_) => Err(Error::custom("path not supported")),
|
||||
Self::Bytes(b) => BytesDeserializer(b.into()).deserialize_any(visitor),
|
||||
Self::Any(_) => Err(Error::custom("any not supported")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,9 +122,9 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
|
||||
Data::String(Cow::Owned(s)) => visitor.visit_string(s),
|
||||
Data::Url(u) => visitor.visit_newtype_struct(u.into_deserializer()),
|
||||
Self::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
|
||||
Self::String(Cow::Owned(s)) => visitor.visit_string(s),
|
||||
Self::Url(u) => visitor.visit_newtype_struct(u.into_deserializer()),
|
||||
_ => Err(Error::custom("not a string")),
|
||||
}
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Bytes(b) => BytesDeserializer(b.into()).deserialize_bytes(visitor),
|
||||
Self::Bytes(b) => BytesDeserializer(b.into()).deserialize_bytes(visitor),
|
||||
_ => Err(Error::custom("not bytes")),
|
||||
}
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Nil => visitor.visit_none(),
|
||||
Self::Nil => visitor.visit_none(),
|
||||
other => visitor.visit_some(other),
|
||||
}
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Nil => visitor.visit_unit(),
|
||||
Self::Nil => visitor.visit_unit(),
|
||||
_ => Err(Error::custom("expected unit")),
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Nil => visitor.visit_unit(),
|
||||
Self::Nil => visitor.visit_unit(),
|
||||
_ => Err(Error::custom("expected unit struct")),
|
||||
}
|
||||
}
|
||||
|
|
@ -203,8 +203,8 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::List(l) => visitor.visit_seq(SeqDeserializer { iter: l.into_iter() }),
|
||||
Data::Bytes(b) => BytesDeserializer(b.into()).deserialize_seq(visitor),
|
||||
Self::List(l) => visitor.visit_seq(SeqDeserializer { iter: l.into_iter() }),
|
||||
Self::Bytes(b) => BytesDeserializer(b.into()).deserialize_seq(visitor),
|
||||
_ => Err(Error::custom("not a sequence")),
|
||||
}
|
||||
}
|
||||
|
|
@ -233,8 +233,8 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::Dict(d) => visitor.visit_map(MapDeserializer { iter: d.into_iter(), value: None }),
|
||||
Data::Url(u) => Deserializer::deserialize_map(u.into_deserializer(), visitor),
|
||||
Self::Dict(d) => visitor.visit_map(MapDeserializer { iter: d.into_iter(), value: None }),
|
||||
Self::Url(u) => Deserializer::deserialize_map(u.into_deserializer(), visitor),
|
||||
_ => Err(Error::custom("not a map")),
|
||||
}
|
||||
}
|
||||
|
|
@ -261,7 +261,7 @@ impl<'de> Deserializer<'de> for Data {
|
|||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self {
|
||||
Data::String(s) => visitor.visit_enum(s.into_deserializer()),
|
||||
Self::String(s) => visitor.visit_enum(s.into_deserializer()),
|
||||
_ => Err(Error::custom("not an enum")),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ use std::{borrow::Cow, fmt::{self, Display}, mem, str::FromStr};
|
|||
use anyhow::{Result, anyhow, bail};
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, de};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{Layer, SStr, Source, data::{Data, DataAny, DataKey}};
|
||||
use crate::{Layer, SStr, Source, data::{Data, DataAny, DataKey}, event::Replier};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Action {
|
||||
|
|
@ -89,7 +88,7 @@ impl Action {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_replier(mut self, tx: mpsc::UnboundedSender<anyhow::Result<Data>>) -> Self {
|
||||
pub fn with_replier(mut self, tx: Replier) -> Self {
|
||||
self.args.insert("replier".into(), Data::Any(Box::new(tx)));
|
||||
self
|
||||
}
|
||||
|
|
@ -115,9 +114,7 @@ impl Action {
|
|||
self.args.get(&name.into())?.as_any()
|
||||
}
|
||||
|
||||
pub fn replier(&self) -> Option<&mpsc::UnboundedSender<anyhow::Result<Data>>> {
|
||||
self.any("replier")
|
||||
}
|
||||
pub fn replier(&self) -> Option<&Replier> { self.any("replier") }
|
||||
|
||||
pub fn first<'a, T>(&'a self) -> Result<T>
|
||||
where
|
||||
|
|
@ -210,9 +207,7 @@ impl Action {
|
|||
(0..self.len()).filter_map(|i| self.args.remove(&DataKey::from(i))?.into_any())
|
||||
}
|
||||
|
||||
pub fn take_replier(&mut self) -> Option<mpsc::UnboundedSender<anyhow::Result<Data>>> {
|
||||
self.take_any("replier")
|
||||
}
|
||||
pub fn take_replier(&mut self) -> Option<Replier> { self.take_any("replier") }
|
||||
|
||||
// Parse
|
||||
pub fn parse_args<I>(words: I, last: Option<String>) -> Result<HashMap<DataKey, Data>>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ use std::{iter, ops::Deref};
|
|||
|
||||
use anyhow::Result;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use super::Action;
|
||||
use crate::data::{Data, DataKey};
|
||||
use crate::{data::{Data, DataKey}, event::Replier};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ActionCow {
|
||||
|
|
@ -116,7 +115,7 @@ impl ActionCow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn take_replier(&mut self) -> Option<mpsc::UnboundedSender<anyhow::Result<Data>>> {
|
||||
pub fn take_replier(&mut self) -> Option<Replier> {
|
||||
match self {
|
||||
Self::Owned(c) => c.take_replier(),
|
||||
Self::Borrowed(_) => None,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
yazi_macro::mod_flat!(action cow de de_owned event);
|
||||
|
||||
pub type Replier = tokio::sync::mpsc::UnboundedSender<anyhow::Result<crate::data::Data>>;
|
||||
|
||||
pub static NEED_RENDER: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue