feat: ya.input() plugin API (#762)

This commit is contained in:
三咲雅 · Misaki Masa 2024-03-02 20:33:34 +08:00 committed by GitHub
parent b39b506e27
commit bd572706cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 550 additions and 286 deletions

14
Cargo.lock generated
View file

@ -2740,6 +2740,7 @@ dependencies = [
"yazi-boot",
"yazi-config",
"yazi-plugin",
"yazi-proxy",
"yazi-scheduler",
"yazi-shared",
]
@ -2770,6 +2771,7 @@ dependencies = [
"yazi-config",
"yazi-core",
"yazi-plugin",
"yazi-proxy",
"yazi-scheduler",
"yazi-shared",
]
@ -2801,6 +2803,7 @@ dependencies = [
"yazi-boot",
"yazi-config",
"yazi-prebuild",
"yazi-proxy",
"yazi-shared",
]
@ -2810,6 +2813,16 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4b6c8e12e39ac0f79fa96f36e5b88e0da8d230691abd729eec709b43c74f632"
[[package]]
name = "yazi-proxy"
version = "0.2.3"
dependencies = [
"anyhow",
"tokio",
"yazi-config",
"yazi-shared",
]
[[package]]
name = "yazi-scheduler"
version = "0.2.3"
@ -2828,6 +2841,7 @@ dependencies = [
"yazi-adaptor",
"yazi-config",
"yazi-plugin",
"yazi-proxy",
"yazi-shared",
]

View file

@ -1,24 +1,63 @@
use std::{fmt::Display, str::FromStr};
use anyhow::bail;
use serde::Deserialize;
#[derive(Clone, Copy, Default, Deserialize, PartialEq, Eq)]
#[serde(try_from = "String")]
pub enum Origin {
#[default]
#[serde(rename = "top-left")]
TopLeft,
#[serde(rename = "top-center")]
TopCenter,
#[serde(rename = "top-right")]
TopRight,
#[serde(rename = "bottom-left")]
BottomLeft,
#[serde(rename = "bottom-center")]
BottomCenter,
#[serde(rename = "bottom-right")]
BottomRight,
#[serde(rename = "center")]
Center,
#[serde(rename = "hovered")]
Hovered,
}
impl FromStr for Origin {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"top-left" => Self::TopLeft,
"top-center" => Self::TopCenter,
"top-right" => Self::TopRight,
"bottom-left" => Self::BottomLeft,
"bottom-center" => Self::BottomCenter,
"bottom-right" => Self::BottomRight,
"center" => Self::Center,
"hovered" => Self::Hovered,
_ => bail!("Invalid `origin` value: {s}"),
})
}
}
impl TryFrom<String> for Origin {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> { Self::from_str(&value) }
}
impl Display for Origin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::TopLeft => "top-left",
Self::TopCenter => "top-center",
Self::TopRight => "top-right",
Self::BottomLeft => "bottom-left",
Self::BottomCenter => "bottom-center",
Self::BottomRight => "bottom-right",
Self::Center => "center",
Self::Hovered => "hovered",
})
}
}

View file

@ -20,7 +20,7 @@ impl FromStr for SortBy {
"none" => Self::None,
"key" => Self::Key,
"desc" => Self::Desc,
_ => bail!("Invalid sort option: {s}"),
_ => bail!("Invalid `sort_by` value: {s}"),
})
}
}

View file

@ -13,6 +13,7 @@ yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" }
yazi-boot = { path = "../yazi-boot", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }

View file

@ -1,6 +1,7 @@
use yazi_shared::{emit, event::Cmd, render, Layer};
use yazi_proxy::InputProxy;
use yazi_shared::{event::Cmd, render};
use crate::{completion::Completion, input::Input};
use crate::completion::Completion;
pub struct Opt {
submit: bool,
@ -11,16 +12,11 @@ impl From<Cmd> for Opt {
}
impl Completion {
#[inline]
pub fn _close() {
emit!(Call(Cmd::new("close"), Layer::Completion));
}
pub fn close(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if let Some(s) = self.selected().filter(|_| opt.submit) {
Input::_complete(s, self.ticket);
InputProxy::complete(s, self.ticket);
}
self.caches.clear();

View file

@ -20,14 +20,6 @@ impl From<Cmd> for Opt {
}
impl Completion {
#[inline]
pub fn _trigger(word: &str, ticket: usize) {
emit!(Call(
Cmd::args("trigger", vec![word.to_owned()]).with("ticket", ticket),
Layer::Completion
));
}
pub fn trigger(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if opt.ticket < self.ticket {

View file

@ -2,10 +2,11 @@ use std::{mem, time::SystemTime};
use ratatui::layout::Rect;
use yazi_config::{LAYOUT, MANAGER};
use yazi_proxy::ManagerProxy;
use yazi_shared::fs::{File, FilesOp, Url};
use super::FolderStage;
use crate::{folder::Files, manager::Manager, Step};
use crate::{folder::Files, Step};
#[derive(Default)]
pub struct Folder {
@ -99,7 +100,7 @@ impl Folder {
let new = self.cursor / limit;
if mem::replace(&mut self.page, new) != new || force {
Manager::_update_paged_by(new, &self.cwd);
ManagerProxy::update_paged_by(new, &self.cwd);
}
}

View file

@ -1,6 +1,7 @@
use yazi_proxy::CompletionProxy;
use yazi_shared::{event::Cmd, render, InputError};
use crate::{completion::Completion, input::Input};
use crate::input::Input;
pub struct Opt {
submit: bool,
@ -18,7 +19,7 @@ impl Input {
let opt = opt.into() as Opt;
if self.completion {
Completion::_close();
CompletionProxy::close();
}
if let Some(cb) = self.callback.take() {

View file

@ -1,6 +1,6 @@
use std::path::MAIN_SEPARATOR;
use yazi_shared::{emit, event::Cmd, render, Layer};
use yazi_shared::{event::Cmd, render};
use crate::input::Input;
@ -19,11 +19,6 @@ impl From<Cmd> for Opt {
}
impl Input {
#[inline]
pub fn _complete(word: &str, ticket: usize) {
emit!(Call(Cmd::args("complete", vec![word.to_owned()]).with("ticket", ticket), Layer::Input));
}
pub fn complete(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if self.ticket != opt.ticket {

View file

@ -1,6 +1,7 @@
use yazi_proxy::CompletionProxy;
use yazi_shared::{event::Cmd, render};
use crate::{completion::Completion, input::{op::InputOp, Input, InputMode}};
use crate::input::{op::InputOp, Input, InputMode};
pub struct Opt;
@ -26,7 +27,7 @@ impl Input {
self.move_(-1);
if self.completion {
Completion::_close();
CompletionProxy::close();
}
}
}

View file

@ -1,28 +1,10 @@
use tokio::sync::mpsc;
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, render, InputError, Layer};
use yazi_proxy::InputOpt;
use yazi_shared::render;
use crate::input::Input;
pub struct Opt {
cfg: InputCfg,
tx: mpsc::UnboundedSender<Result<String, InputError>>,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl Input {
pub fn _show(cfg: InputCfg) -> mpsc::UnboundedReceiver<Result<String, InputError>> {
let (tx, rx) = mpsc::unbounded_channel();
emit!(Call(Cmd::new("show").with_data(Opt { cfg, tx }), Layer::Input));
rx
}
pub fn show(&mut self, opt: impl TryInto<Opt>) {
pub fn show(&mut self, opt: impl TryInto<InputOpt>) {
let Ok(opt) = opt.try_into() else {
return;
};

View file

@ -2,9 +2,10 @@ use std::path::PathBuf;
use tokio::fs;
use yazi_config::popup::InputCfg;
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{event::Cmd, fs::{File, FilesOp, Url}};
use crate::{input::Input, manager::Manager};
use crate::manager::Manager;
pub struct Opt {
force: bool,
@ -19,14 +20,14 @@ impl Manager {
let opt = opt.into() as Opt;
let cwd = self.cwd().to_owned();
tokio::spawn(async move {
let mut result = Input::_show(InputCfg::create());
let mut result = InputProxy::show(InputCfg::create());
let Some(Ok(name)) = result.recv().await else {
return Ok(());
};
let path = cwd.join(&name);
if !opt.force && fs::symlink_metadata(&path).await.is_ok() {
match Input::_show(InputCfg::overwrite()).recv().await {
match InputProxy::show(InputCfg::overwrite()).recv().await {
Some(Ok(c)) if c == "y" || c == "Y" => (),
_ => return Ok(()),
}
@ -43,7 +44,7 @@ impl Manager {
Url::from(path.components().take(cwd.components().count() + 1).collect::<PathBuf>());
if let Ok(f) = File::from(child.clone()).await {
FilesOp::Creating(cwd, vec![f]).emit();
Manager::_hover(Some(child));
ManagerProxy::hover(Some(child));
}
Ok::<(), anyhow::Error>(())
});

View file

@ -1,6 +1,6 @@
use std::collections::BTreeSet;
use yazi_shared::{emit, event::Cmd, fs::Url, render, Layer};
use yazi_shared::{event::Cmd, fs::Url, render};
use crate::manager::Manager;
@ -16,14 +16,6 @@ impl From<Option<Url>> for Opt {
}
impl Manager {
#[inline]
pub fn _hover(url: Option<Url>) {
emit!(Call(
Cmd::args("hover", url.map_or_else(Vec::new, |u| vec![u.to_string()])),
Layer::Manager
));
}
pub fn hover(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;

View file

@ -4,9 +4,10 @@ use tracing::error;
use yazi_boot::ARGS;
use yazi_config::{popup::SelectCfg, OPEN};
use yazi_plugin::isolate;
use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, Layer, MIME_DIR};
use yazi_proxy::{ManagerProxy, OpenDoOpt, TasksProxy};
use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, MIME_DIR};
use crate::{folder::Folder, manager::Manager, select::Select, tasks::Tasks};
use crate::{folder::Folder, manager::Manager, tasks::Tasks};
pub struct Opt {
interactive: bool,
@ -22,17 +23,6 @@ impl From<Cmd> for Opt {
}
}
#[derive(Default)]
pub struct OptDo {
hovered: Url,
targets: Vec<(Url, String)>,
interactive: bool,
}
impl From<Cmd> for OptDo {
fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() }
}
impl Manager {
pub fn open(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
if !self.active_mut().try_escape_visual() {
@ -50,7 +40,7 @@ impl Manager {
let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]);
for u in selected {
if self.mimetype.get(u).is_some() {
if self.mimetype.contains_key(u) {
done.push((u.clone(), String::new()));
} else if self.guess_folder(u) {
done.push((u.clone(), MIME_DIR.to_owned()));
@ -60,7 +50,8 @@ impl Manager {
}
if todo.is_empty() {
return self.open_do(OptDo { hovered, targets: done, interactive: opt.interactive }, tasks);
return self
.open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }, tasks);
}
tokio::spawn(async move {
@ -76,17 +67,12 @@ impl Manager {
error!("preload in open failed: {e}");
}
Self::_open_do(OptDo { hovered, targets: done, interactive: opt.interactive });
ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive });
});
}
#[inline]
pub fn _open_do(opt: OptDo) {
emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager));
}
pub fn open_do(&mut self, opt: impl Into<OptDo>, tasks: &Tasks) {
let opt = opt.into() as OptDo;
pub fn open_do(&mut self, opt: impl Into<OpenDoOpt>, tasks: &Tasks) {
let opt = opt.into() as OpenDoOpt;
let targets: Vec<_> = opt
.targets
.into_iter()
@ -108,9 +94,11 @@ impl Manager {
let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect();
tokio::spawn(async move {
let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect()));
let result = yazi_proxy::SelectProxy::show(SelectCfg::open(
openers.iter().map(|o| o.desc.clone()).collect(),
));
if let Ok(choice) = result.await {
Tasks::_open_with(urls, openers[choice].clone());
TasksProxy::open_with(urls, openers[choice].clone());
}
});
}

View file

@ -1,4 +1,4 @@
use yazi_shared::{emit, event::Cmd, fs::Url, render, Layer};
use yazi_shared::{event::Cmd, fs::Url, render};
use crate::manager::Manager;
@ -25,11 +25,6 @@ impl From<bool> for Opt {
}
impl Manager {
#[inline]
pub fn _peek(force: bool) {
emit!(Call(Cmd::new("peek").with_bool("force", force), Layer::Manager));
}
pub fn peek(&mut self, opt: impl Into<Opt>) {
let Some(hovered) = self.hovered().cloned() else {
return render!(self.active_mut().preview.reset());

View file

@ -1,7 +1,8 @@
use yazi_config::popup::InputCfg;
use yazi_proxy::InputProxy;
use yazi_shared::{emit, event::{Cmd, EventQuit}};
use crate::{input::Input, manager::Manager, tasks::Tasks};
use crate::{manager::Manager, tasks::Tasks};
#[derive(Default)]
pub struct Opt {
@ -25,7 +26,7 @@ impl Manager {
}
tokio::spawn(async move {
let mut result = Input::_show(InputCfg::quit(tasks));
let mut result = InputProxy::show(InputCfg::quit(tasks));
if let Some(Ok(choice)) = result.recv().await {
if choice == "y" || choice == "Y" {
emit!(Quit(opt));

View file

@ -1,15 +1,10 @@
use std::env;
use yazi_shared::{emit, event::Cmd, Layer};
use yazi_shared::event::Cmd;
use crate::{manager::Manager, tasks::Tasks};
impl Manager {
#[inline]
pub fn _refresh() {
emit!(Call(Cmd::new("refresh"), Layer::Manager));
}
pub fn refresh(&mut self, _: Cmd, tasks: &Tasks) {
env::set_current_dir(self.cwd()).ok();
env::set_var("PWD", self.cwd());

View file

@ -1,7 +1,8 @@
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{event::Cmd, fs::Url};
use crate::{input::Input, manager::Manager, tasks::Tasks};
use crate::{manager::Manager, tasks::Tasks};
pub struct Opt {
force: bool,
@ -33,7 +34,7 @@ impl Manager {
}
tokio::spawn(async move {
let mut result = Input::_show(if opt.permanently {
let mut result = InputProxy::show(if opt.permanently {
InputCfg::delete(opt.targets.len())
} else {
InputCfg::trash(opt.targets.len())
@ -44,19 +45,11 @@ impl Manager {
return;
}
Self::_remove_do(opt.targets, opt.permanently);
ManagerProxy::remove_do(opt.targets, opt.permanently);
}
});
}
#[inline]
pub fn _remove_do(targets: Vec<Url>, permanently: bool) {
emit!(Call(
Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets),
Layer::Manager
));
}
pub fn remove_do(&mut self, opt: impl Into<Opt>, tasks: &Tasks) {
let opt = opt.into() as Opt;
for u in &opt.targets {

View file

@ -4,10 +4,11 @@ use anyhow::{anyhow, bail, Result};
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
use yazi_config::{popup::InputCfg, OPEN, PREVIEW};
use yazi_plugin::external::{self, ShellOpt};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::{AppProxy, InputProxy, ManagerProxy};
use yazi_scheduler::BLOCKER;
use yazi_shared::{event::Cmd, fs::{max_common_root, File, FilesOp, Url}, term::Term, Defer};
use crate::{input::Input, manager::Manager};
use crate::manager::Manager;
pub struct Opt {
force: bool,
@ -49,7 +50,7 @@ impl Manager {
let file = File::from(new.clone()).await?;
FilesOp::Deleting(file.parent().unwrap(), vec![new.clone()]).emit();
FilesOp::Upserting(file.parent().unwrap(), BTreeMap::from_iter([(old, file)])).emit();
Ok(Self::_hover(Some(new)))
Ok(ManagerProxy::hover(Some(new)))
}
pub fn rename(&mut self, opt: impl Into<Opt>) {
@ -77,7 +78,7 @@ impl Manager {
};
tokio::spawn(async move {
let mut result = Input::_show(InputCfg::rename().with_value(name).with_cursor(cursor));
let mut result = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor));
let Some(Ok(name)) = result.recv().await else {
return;
};
@ -88,7 +89,7 @@ impl Manager {
return;
}
let mut result = Input::_show(InputCfg::overwrite());
let mut result = InputProxy::show(InputCfg::overwrite());
if let Some(Ok(choice)) = result.recv().await {
if choice == "y" || choice == "Y" {
Self::rename_and_hover(hovered, Url::from(new)).await.ok();
@ -122,10 +123,10 @@ impl Manager {
let _guard = BLOCKER.acquire().await.unwrap();
let _defer = Defer::new(|| {
Scheduler::app_resume();
AppProxy::resume();
tokio::spawn(fs::remove_file(tmp.clone()))
});
Scheduler::app_stop().await;
AppProxy::stop().await;
let mut child = external::shell(ShellOpt {
cmd: (*opener.exec).into(),

View file

@ -1,4 +1,4 @@
use yazi_scheduler::Scheduler;
use yazi_proxy::AppProxy;
use yazi_shared::event::Cmd;
use crate::manager::Manager;
@ -7,7 +7,7 @@ impl Manager {
pub fn suspend(&mut self, _: Cmd) {
#[cfg(unix)]
tokio::spawn(async move {
Scheduler::app_stop().await;
AppProxy::stop().await;
unsafe { libc::raise(libc::SIGTSTP) };
});
}

View file

@ -1,5 +1,6 @@
use std::borrow::Cow;
use yazi_proxy::ManagerProxy;
use yazi_shared::{event::Cmd, fs::FilesOp, render};
use crate::{folder::Folder, manager::Manager, tab::Tab, tasks::Tasks};
@ -56,8 +57,8 @@ impl Manager {
return;
}
Self::_hover(None); // Re-hover in next loop
Self::_update_paged(); // Update for paged files in next loop
ManagerProxy::hover(None); // Re-hover in next loop
ManagerProxy::update_paged(); // Update for paged files in next loop
if calc {
tasks.preload_sorted(&tab.current.files);
}
@ -73,7 +74,7 @@ impl Manager {
}
if !foreign {
Self::_peek(true);
ManagerProxy::peek(true);
}
}

View file

@ -1,4 +1,4 @@
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use yazi_shared::{event::Cmd, fs::Url};
use crate::{manager::Manager, tasks::Tasks};
@ -22,19 +22,6 @@ impl From<()> for Opt {
}
impl Manager {
#[inline]
pub fn _update_paged() {
emit!(Call(Cmd::new("update_paged"), Layer::Manager));
}
#[inline]
pub fn _update_paged_by(page: usize, only_if: &Url) {
emit!(Call(
Cmd::args("update_paged", vec![page.to_string()]).with("only-if", only_if.to_string()),
Layer::Manager
));
}
pub fn update_paged(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) {
let Ok(opt) = opt.try_into() else {
return;

View file

@ -1,9 +1,10 @@
use std::ops::{Deref, DerefMut};
use yazi_boot::BOOT;
use yazi_proxy::ManagerProxy;
use yazi_shared::fs::Url;
use crate::{manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Tabs {
pub idx: usize,
@ -17,7 +18,7 @@ impl Tabs {
tabs.items[0].reveal(Url::from(BOOT.cwd.join(file)));
}
Manager::_refresh();
ManagerProxy::refresh();
tabs
}
@ -42,8 +43,8 @@ impl Tabs {
}
self.idx = idx;
Manager::_refresh();
Manager::_peek(true);
ManagerProxy::refresh();
ManagerProxy::peek(true);
}
}

View file

@ -5,18 +5,6 @@ use yazi_shared::{emit, event::Cmd, Layer};
use crate::notify::{Message, Notify};
impl Notify {
#[inline]
pub fn _push_warn(title: &str, content: &str) {
emit!(Call(
Cmd::new("notify")
.with("title", title)
.with("content", content)
.with("level", "warn")
.with("timeout", 5),
Layer::App
));
}
pub fn push(&mut self, msg: impl TryInto<Message>) {
let Ok(mut msg) = msg.try_into() else {
return;

View file

@ -1,29 +1,10 @@
use anyhow::Result;
use tokio::sync::oneshot;
use yazi_config::popup::SelectCfg;
use yazi_shared::{emit, event::Cmd, render, term::Term, Layer};
use yazi_proxy::SelectOpt;
use yazi_shared::render;
use crate::select::Select;
pub struct Opt {
cfg: SelectCfg,
tx: oneshot::Sender<Result<usize>>,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl Select {
pub async fn _show(cfg: SelectCfg) -> Result<usize> {
let (tx, rx) = oneshot::channel();
emit!(Call(Cmd::new("show").with_data(Opt { cfg, tx }), Layer::Select));
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
}
pub fn show(&mut self, opt: impl TryInto<Opt>) {
pub fn show(&mut self, opt: impl TryInto<SelectOpt>) {
let Ok(opt) = opt.try_into() else {
return;
};

View file

@ -1,6 +1,7 @@
use yazi_proxy::ManagerProxy;
use yazi_shared::{event::Cmd, render};
use crate::{manager::Manager, tab::Tab, Step};
use crate::{tab::Tab, Step};
pub struct Opt {
step: Step,
@ -36,7 +37,7 @@ impl Tab {
}
}
Manager::_hover(None);
ManagerProxy::hover(None);
render!();
}
}

View file

@ -3,9 +3,10 @@ use std::{mem, time::Duration};
use tokio::{fs, pin};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, fs::{expand_path, Url}, render, Debounce, InputError, Layer};
use yazi_proxy::{CompletionProxy, InputProxy, ManagerProxy, TabProxy};
use yazi_shared::{event::Cmd, fs::{expand_path, Url}, render, Debounce, InputError};
use crate::{completion::Completion, input::Input, manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Opt {
target: Url,
@ -27,11 +28,6 @@ impl From<Url> for Opt {
}
impl Tab {
#[inline]
pub fn _cd(target: &Url) {
emit!(Call(Cmd::args("cd", vec![target.to_string()]), Layer::Manager));
}
pub fn cd(&mut self, opt: impl Into<Opt>) {
if !self.try_escape_visual() {
return;
@ -68,13 +64,13 @@ impl Tab {
self.backstack.push(opt.target.clone());
}
Manager::_refresh();
ManagerProxy::refresh();
render!();
}
fn cd_interactive(&mut self) {
tokio::spawn(async move {
let rx = Input::_show(InputCfg::cd());
let rx = InputProxy::show(InputCfg::cd());
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
pin!(rx);
@ -88,13 +84,13 @@ impl Tab {
};
if meta.is_dir() {
Tab::_cd(&u);
TabProxy::cd(&u);
} else {
Tab::_reveal(&u);
TabProxy::reveal(&u);
}
}
Err(InputError::Completed(before, ticket)) => {
Completion::_trigger(&before, ticket);
CompletionProxy::trigger(&before, ticket);
}
_ => break,
}

View file

@ -1,7 +1,8 @@
use bitflags::bitflags;
use yazi_proxy::{AppProxy, ManagerProxy};
use yazi_shared::{event::Cmd, render, render_and};
use crate::{manager::Manager, notify::Notify, tab::Tab};
use crate::tab::Tab;
bitflags! {
pub struct Opt: u8 {
@ -74,7 +75,7 @@ impl Tab {
self.selected.clear();
if self.current.hovered().is_some_and(|h| h.is_dir()) {
Manager::_peek(true);
ManagerProxy::peek(true);
}
render_and!(true)
}
@ -110,7 +111,7 @@ impl Tab {
if !select {
self.selected.remove_many(&urls);
} else if self.selected.add_many(&urls) != urls.len() {
Notify::_push_warn(
AppProxy::warn(
"Escape visual mode",
"Some files cannot be selected, due to path nesting conflict.",
);

View file

@ -3,9 +3,10 @@ use std::time::Duration;
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_proxy::{InputProxy, ManagerProxy};
use yazi_shared::{emit, event::Cmd, render, Debounce, InputError, Layer};
use crate::{folder::{Filter, FilterCase}, input::Input, manager::Manager, tab::Tab};
use crate::{folder::{Filter, FilterCase}, tab::Tab};
#[derive(Default)]
pub struct Opt {
@ -28,7 +29,7 @@ impl Tab {
pub fn filter(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
tokio::spawn(async move {
let rx = Input::_show(InputCfg::filter());
let rx = InputProxy::show(InputCfg::filter());
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
pin!(rx);
@ -62,7 +63,7 @@ impl Tab {
};
if opt.done {
Manager::_update_paged(); // Update for paged files in next loop
ManagerProxy::update_paged(); // Update for paged files in next loop
}
let hovered = self.current.hovered().map(|f| f.url());
@ -71,7 +72,7 @@ impl Tab {
}
if self.current.repos(hovered) {
Manager::_hover(None);
ManagerProxy::hover(None);
}
render!();
}

View file

@ -3,9 +3,10 @@ use std::time::Duration;
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_proxy::InputProxy;
use yazi_shared::{emit, event::Cmd, render, Debounce, InputError, Layer};
use crate::{folder::FilterCase, input::Input, tab::{Finder, Tab}};
use crate::{folder::FilterCase, tab::{Finder, Tab}};
pub struct Opt {
query: Option<String>,
@ -35,7 +36,7 @@ impl Tab {
pub fn find(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
tokio::spawn(async move {
let rx = Input::_show(InputCfg::find(opt.prev));
let rx = InputProxy::show(InputCfg::find(opt.prev));
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
pin!(rx);

View file

@ -1,6 +1,7 @@
use yazi_proxy::ManagerProxy;
use yazi_shared::event::Cmd;
use crate::{manager::Manager, tab::Tab};
use crate::tab::Tab;
impl Tab {
pub fn hidden(&mut self, c: Cmd) {
@ -14,10 +15,10 @@ impl Tab {
self.apply_files_attrs();
if hovered.as_ref() != self.current.hovered().map(|f| &f.url) {
Manager::_hover(hovered);
ManagerProxy::hover(hovered);
} else if self.current.hovered().is_some_and(|f| f.is_dir()) {
Manager::_peek(true);
ManagerProxy::peek(true);
}
Manager::_update_paged();
ManagerProxy::update_paged();
}
}

View file

@ -1,5 +1,6 @@
use yazi_plugin::external::{self, FzfOpt, ZoxideOpt};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::{AppProxy, TabProxy};
use yazi_scheduler::BLOCKER;
use yazi_shared::{event::Cmd, fs::ends_with_slash, Defer};
use crate::tab::Tab;
@ -37,8 +38,8 @@ impl Tab {
let cwd = self.current.cwd.clone();
tokio::spawn(async move {
let _guard = BLOCKER.acquire().await.unwrap();
let _defer = Defer::new(Scheduler::app_resume);
Scheduler::app_stop().await;
let _defer = Defer::new(AppProxy::resume);
AppProxy::stop().await;
let result = if opt.type_ == OptType::Fzf {
external::fzf(FzfOpt { cwd }).await
@ -51,9 +52,9 @@ impl Tab {
};
if opt.type_ == OptType::Fzf && !ends_with_slash(&url) {
Tab::_reveal(&url)
TabProxy::reveal(&url)
} else {
Tab::_cd(&url)
TabProxy::cd(&url)
}
});
}

View file

@ -1,6 +1,7 @@
use yazi_shared::{emit, event::Cmd, fs::{expand_path, File, FilesOp, Url}, Layer};
use yazi_proxy::ManagerProxy;
use yazi_shared::{event::Cmd, fs::{expand_path, File, FilesOp, Url}};
use crate::{manager::Manager, tab::Tab};
use crate::tab::Tab;
pub struct Opt {
target: Url,
@ -21,11 +22,6 @@ impl From<Url> for Opt {
}
impl Tab {
#[inline]
pub fn _reveal(target: &Url) {
emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager));
}
pub fn reveal(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
@ -35,6 +31,6 @@ impl Tab {
self.cd(parent.clone());
FilesOp::Creating(parent, vec![File::from_dummy(&opt.target)]).emit();
Manager::_hover(Some(opt.target));
ManagerProxy::hover(Some(opt.target));
}
}

View file

@ -5,9 +5,10 @@ use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::popup::InputCfg;
use yazi_plugin::external;
use yazi_proxy::{InputProxy, ManagerProxy, TabProxy};
use yazi_shared::{event::Cmd, fs::FilesOp, render};
use crate::{input::Input, manager::Manager, tab::Tab};
use crate::tab::Tab;
#[derive(PartialEq, Eq)]
pub enum OptType {
@ -59,7 +60,7 @@ impl Tab {
let hidden = self.conf.show_hidden;
self.search = Some(tokio::spawn(async move {
let mut input = Input::_show(InputCfg::search(&opt.type_.to_string()));
let mut input = InputProxy::show(InputCfg::search(&opt.type_.to_string()));
let Some(Ok(subject)) = input.recv().await else { bail!("") };
cwd = cwd.into_search(subject.clone());
@ -72,7 +73,7 @@ impl Tab {
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300));
pin!(rx);
let ((), ticket) = (Tab::_cd(&cwd), FilesOp::prepare(&cwd));
let ((), ticket) = (TabProxy::cd(&cwd), FilesOp::prepare(&cwd));
while let Some(chunk) = rx.next().await {
FilesOp::Part(cwd.clone(), chunk, ticket).emit();
}
@ -90,7 +91,7 @@ impl Tab {
if self.current.cwd.is_search() {
let rep = self.history_new(&self.current.cwd.to_regular());
drop(mem::replace(&mut self.current, rep));
Manager::_refresh();
ManagerProxy::refresh();
}
}
}

View file

@ -1,8 +1,9 @@
use std::borrow::Cow;
use yazi_proxy::AppProxy;
use yazi_shared::{event::Cmd, fs::Url, render, render_and};
use crate::{notify::Notify, tab::Tab};
use crate::tab::Tab;
pub struct Opt<'a> {
url: Option<Cow<'a, Url>>,
@ -37,10 +38,7 @@ impl<'a> Tab {
};
if !b {
Notify::_push_warn(
"Select one",
"This file cannot be selected, due to path nesting conflict.",
);
AppProxy::warn("Select one", "This file cannot be selected, due to path nesting conflict.");
}
}
}

View file

@ -1,6 +1,7 @@
use yazi_proxy::AppProxy;
use yazi_shared::{event::Cmd, render};
use crate::{notify::Notify, tab::Tab};
use crate::tab::Tab;
pub struct Opt {
state: Option<bool>,
@ -35,10 +36,7 @@ impl Tab {
render!(added > 0);
if added != addition.len() {
Notify::_push_warn(
"Select all",
"Some files cannot be selected, due to path nesting conflict.",
);
AppProxy::warn("Select all", "Some files cannot be selected, due to path nesting conflict.");
}
}
}

View file

@ -1,7 +1,8 @@
use yazi_config::{open::Opener, popup::InputCfg};
use yazi_proxy::{InputProxy, TasksProxy};
use yazi_shared::event::Cmd;
use crate::{input::Input, tab::Tab, tasks::Tasks};
use crate::tab::Tab;
pub struct Opt {
exec: String,
@ -30,14 +31,14 @@ impl Tab {
tokio::spawn(async move {
if !opt.confirm || opt.exec.is_empty() {
let mut result = Input::_show(InputCfg::shell(opt.block).with_value(opt.exec));
let mut result = InputProxy::show(InputCfg::shell(opt.block).with_value(opt.exec));
match result.recv().await {
Some(Ok(e)) => opt.exec = e,
_ => return,
}
}
Tasks::_open_with(selected, Opener {
TasksProxy::open_with(selected, Opener {
exec: opt.exec,
block: opt.block,
orphan: false,

View file

@ -1,9 +1,10 @@
use std::str::FromStr;
use yazi_config::manager::SortBy;
use yazi_proxy::ManagerProxy;
use yazi_shared::event::Cmd;
use crate::{manager::Manager, tab::Tab, tasks::Tasks};
use crate::{tab::Tab, tasks::Tasks};
impl Tab {
pub fn sort(&mut self, c: Cmd, tasks: &Tasks) {
@ -15,7 +16,7 @@ impl Tab {
self.conf.sort_dir_first = c.named.contains_key("dir-first");
self.apply_files_attrs();
Manager::_update_paged();
ManagerProxy::update_paged();
tasks.preload_sorted(&self.current.files);
}

View file

@ -2,7 +2,8 @@ use std::io::{stdout, Write};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::AppProxy;
use yazi_scheduler::BLOCKER;
use yazi_shared::{event::Cmd, term::Term, Defer};
use crate::tasks::Tasks;
@ -26,10 +27,10 @@ impl Tasks {
task.logs.clone()
};
Scheduler::app_stop().await;
AppProxy::stop().await;
let _defer = Defer::new(|| {
disable_raw_mode().ok();
Scheduler::app_resume();
AppProxy::resume();
});
Term::clear(&mut stdout()).ok();

View file

@ -1,25 +1,9 @@
use yazi_config::open::Opener;
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
use yazi_proxy::OpenWithOpt;
use crate::tasks::Tasks;
pub struct Opt {
targets: Vec<Url>,
opener: Opener,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl Tasks {
pub fn _open_with(targets: Vec<Url>, opener: Opener) {
emit!(Call(Cmd::new("open_with").with_data(Opt { targets, opener }), Layer::Tasks));
}
pub fn open_with(&mut self, opt: impl TryInto<Opt>) {
pub fn open_with(&mut self, opt: impl TryInto<OpenWithOpt>) {
if let Ok(opt) = opt.try_into() {
self.file_open_with(&opt.opener, &opt.targets);
}

View file

@ -14,6 +14,7 @@ yazi-boot = { path = "../yazi-boot", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-core = { path = "../yazi-core", version = "0.2.3" }
yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }

View file

@ -19,7 +19,7 @@ impl<'a> Widget for Layout<'a> {
let chunks = layout::Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).split(area);
Line::styled(
help.keyword().unwrap_or_else(|| format!("{}.help", help.layer.to_string())),
help.keyword().unwrap_or_else(|| format!("{}.help", help.layer)),
THEME.help.footer,
)
.render(chunks[1], buf);

View file

@ -43,7 +43,8 @@ impl Signals {
#[cfg(unix)]
fn spawn_system_task(&self) -> Result<JoinHandle<()>> {
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM};
use yazi_scheduler::{Scheduler, BLOCKER};
use yazi_proxy::AppProxy;
use yazi_scheduler::BLOCKER;
let mut signals = signal_hook_tokio::Signals::new([
// Terminating signals
@ -65,7 +66,7 @@ impl Signals {
break;
}
}
SIGCONT => Scheduler::app_resume(),
SIGCONT => AppProxy::resume(),
_ => {}
}
}

View file

@ -12,6 +12,7 @@ repository = "https://github.com/sxyazi/yazi"
yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" }
yazi-boot = { path = "../yazi-boot", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }
# External dependencies

View file

@ -0,0 +1,32 @@
use mlua::{prelude::LuaUserDataMethods, UserData};
use tokio::sync::mpsc::UnboundedReceiver;
use yazi_shared::InputError;
pub struct InputRx {
inner: UnboundedReceiver<Result<String, InputError>>,
}
impl InputRx {
pub fn new(inner: UnboundedReceiver<Result<String, InputError>>) -> Self { Self { inner } }
pub fn parse(res: Result<String, InputError>) -> (Option<String>, u8) {
match res {
Ok(s) => (Some(s), 1),
Err(InputError::Canceled(s)) => (Some(s), 2),
Err(InputError::Typed(s)) => (Some(s), 3),
_ => (None, 0),
}
}
}
impl UserData for InputRx {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method_mut("recv", |_, me, ()| async move {
let Some(res) = me.inner.recv().await else {
return Ok((None, 0));
};
Ok(Self::parse(res))
});
}
}

View file

@ -4,6 +4,8 @@ mod bindings;
mod cha;
mod file;
mod icon;
mod input;
mod position;
mod range;
mod window;
@ -11,5 +13,7 @@ pub use bindings::*;
pub use cha::*;
pub use file::*;
pub use icon::*;
pub use input::*;
pub use position::*;
pub use range::*;
pub use window::*;

View file

@ -0,0 +1,47 @@
use std::{ops::Deref, str::FromStr};
use mlua::{ExternalResult, IntoLua};
pub struct Position(yazi_config::popup::Position);
impl Deref for Position {
type Target = yazi_config::popup::Position;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl From<Position> for yazi_config::popup::Position {
fn from(value: Position) -> Self { value.0 }
}
impl<'a> TryFrom<mlua::Table<'a>> for Position {
type Error = mlua::Error;
fn try_from(t: mlua::Table<'a>) -> Result<Self, Self::Error> {
use yazi_config::popup::{Offset, Origin, Position};
Ok(Self(Position {
origin: Origin::from_str(t.raw_get::<_, mlua::String>(1)?.to_str()?).into_lua_err()?,
offset: Offset {
x: t.raw_get("x").unwrap_or_default(),
y: t.raw_get("y").unwrap_or_default(),
width: t.raw_get("w").unwrap_or_default(),
height: 3,
},
}))
}
}
impl<'lua> IntoLua<'lua> for Position {
fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value> {
lua
.create_table_from([
(1.into_lua(lua)?, self.origin.to_string().into_lua(lua)?),
("x".into_lua(lua)?, self.offset.x.into_lua(lua)?),
("y".into_lua(lua)?, self.offset.y.into_lua(lua)?),
("w".into_lua(lua)?, self.offset.width.into_lua(lua)?),
("h".into_lua(lua)?, self.offset.height.into_lua(lua)?),
])?
.into_lua(lua)
}
}

View file

@ -1,11 +1,13 @@
use std::str::FromStr;
use mlua::{ExternalError, ExternalResult, Lua, Table, Value};
use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value};
use tokio::sync::mpsc;
use yazi_config::keymap::{Control, Key};
use yazi_config::{keymap::{Control, Key}, popup::InputCfg};
use yazi_proxy::InputProxy;
use yazi_shared::{emit, event::Cmd, Layer};
use super::Utils;
use crate::bindings::{InputRx, Position};
impl Utils {
fn parse_keys(value: Value) -> mlua::Result<Vec<Key>> {
@ -53,6 +55,30 @@ impl Utils {
})?,
)?;
ya.raw_set(
"input",
lua.create_async_function(|lua, t: Table| async move {
let realtime = t.raw_get("realtime").unwrap_or_default();
let mut rx = InputProxy::show(InputCfg {
title: t.raw_get("title")?,
value: t.raw_get("value").unwrap_or_default(),
cursor: None, // TODO
position: Position::try_from(t.raw_get::<_, Table>("position")?)?.into(),
realtime,
completion: false,
highlight: false,
});
if realtime {
(InputRx::new(rx), Value::Nil).into_lua_multi(lua)
} else if let Some(res) = rx.recv().await {
InputRx::parse(res).into_lua_multi(lua)
} else {
(Value::Nil, 0).into_lua_multi(lua)
}
})?,
)?;
Ok(())
}
}

17
yazi-proxy/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "yazi-proxy"
version = "0.2.3"
edition = "2021"
license = "MIT"
authors = [ "sxyazi <sxyazi@gmail.com>" ]
description = "Yazi event proxy"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
[dependencies]
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }
# External dependencies
anyhow = "^1"
tokio = { version = "^1", features = [ "parking_lot" ] }

30
yazi-proxy/src/app.rs Normal file
View file

@ -0,0 +1,30 @@
use tokio::sync::oneshot;
use yazi_shared::{emit, event::Cmd, Layer};
pub struct AppProxy;
impl AppProxy {
#[inline]
pub async fn stop() {
let (tx, rx) = oneshot::channel::<()>();
emit!(Call(Cmd::new("stop").with_data(tx), Layer::App));
rx.await.ok();
}
#[inline]
pub fn resume() {
emit!(Call(Cmd::new("resume"), Layer::App));
}
#[inline]
pub fn warn(title: &str, content: &str) {
emit!(Call(
Cmd::new("notify")
.with("title", title)
.with("content", content)
.with("level", "warn")
.with("timeout", 5),
Layer::App
));
}
}

View file

@ -0,0 +1,18 @@
use yazi_shared::{emit, event::Cmd, Layer};
pub struct CompletionProxy;
impl CompletionProxy {
#[inline]
pub fn close() {
emit!(Call(Cmd::new("close"), Layer::Completion));
}
#[inline]
pub fn trigger(word: &str, ticket: usize) {
emit!(Call(
Cmd::args("trigger", vec![word.to_owned()]).with("ticket", ticket),
Layer::Completion
));
}
}

30
yazi-proxy/src/input.rs Normal file
View file

@ -0,0 +1,30 @@
use tokio::sync::mpsc;
use yazi_config::popup::InputCfg;
use yazi_shared::{emit, event::Cmd, InputError, Layer};
pub struct InputOpt {
pub cfg: InputCfg,
pub tx: mpsc::UnboundedSender<Result<String, InputError>>,
}
impl TryFrom<Cmd> for InputOpt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
pub struct InputProxy;
impl InputProxy {
#[inline]
pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver<Result<String, InputError>> {
let (tx, rx) = mpsc::unbounded_channel();
emit!(Call(Cmd::new("show").with_data(InputOpt { cfg, tx }), Layer::Input));
rx
}
#[inline]
pub fn complete(word: &str, ticket: usize) {
emit!(Call(Cmd::args("complete", vec![word.to_owned()]).with("ticket", ticket), Layer::Input));
}
}

15
yazi-proxy/src/lib.rs Normal file
View file

@ -0,0 +1,15 @@
mod app;
mod completion;
mod input;
mod manager;
mod select;
mod tab;
mod tasks;
pub use app::*;
pub use completion::*;
pub use input::*;
pub use manager::*;
pub use select::*;
pub use tab::*;
pub use tasks::*;

60
yazi-proxy/src/manager.rs Normal file
View file

@ -0,0 +1,60 @@
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
#[derive(Default)]
pub struct OpenDoOpt {
pub hovered: Url,
pub targets: Vec<(Url, String)>,
pub interactive: bool,
}
impl From<Cmd> for OpenDoOpt {
fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() }
}
pub struct ManagerProxy;
impl ManagerProxy {
#[inline]
pub fn peek(force: bool) {
emit!(Call(Cmd::new("peek").with_bool("force", force), Layer::Manager));
}
#[inline]
pub fn hover(url: Option<Url>) {
emit!(Call(
Cmd::args("hover", url.map_or_else(Vec::new, |u| vec![u.to_string()])),
Layer::Manager
));
}
#[inline]
pub fn refresh() {
emit!(Call(Cmd::new("refresh"), Layer::Manager));
}
#[inline]
pub fn open_do(opt: OpenDoOpt) {
emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager));
}
#[inline]
pub fn remove_do(targets: Vec<Url>, permanently: bool) {
emit!(Call(
Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets),
Layer::Manager
));
}
#[inline]
pub fn update_paged() {
emit!(Call(Cmd::new("update_paged"), Layer::Manager));
}
#[inline]
pub fn update_paged_by(page: usize, only_if: &Url) {
emit!(Call(
Cmd::args("update_paged", vec![page.to_string()]).with("only-if", only_if.to_string()),
Layer::Manager
));
}
}

25
yazi-proxy/src/select.rs Normal file
View file

@ -0,0 +1,25 @@
use tokio::sync::oneshot;
use yazi_config::popup::SelectCfg;
use yazi_shared::{emit, event::Cmd, term::Term, Layer};
pub struct SelectOpt {
pub cfg: SelectCfg,
pub tx: oneshot::Sender<anyhow::Result<usize>>,
}
impl TryFrom<Cmd> for SelectOpt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
pub struct SelectProxy;
impl SelectProxy {
#[inline]
pub async fn show(cfg: SelectCfg) -> anyhow::Result<usize> {
let (tx, rx) = oneshot::channel();
emit!(Call(Cmd::new("show").with_data(SelectOpt { cfg, tx }), Layer::Select));
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
}
}

15
yazi-proxy/src/tab.rs Normal file
View file

@ -0,0 +1,15 @@
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
pub struct TabProxy;
impl TabProxy {
#[inline]
pub fn cd(target: &Url) {
emit!(Call(Cmd::args("cd", vec![target.to_string()]), Layer::Manager));
}
#[inline]
pub fn reveal(target: &Url) {
emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager));
}
}

22
yazi-proxy/src/tasks.rs Normal file
View file

@ -0,0 +1,22 @@
use yazi_config::open::Opener;
use yazi_shared::{emit, event::Cmd, fs::Url, Layer};
pub struct TasksProxy;
pub struct OpenWithOpt {
pub targets: Vec<Url>,
pub opener: Opener,
}
impl TryFrom<Cmd> for OpenWithOpt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> { c.take_data().ok_or(()) }
}
impl TasksProxy {
#[inline]
pub fn open_with(targets: Vec<Url>, opener: Opener) {
emit!(Call(Cmd::new("open_with").with_data(OpenWithOpt { targets, opener }), Layer::Tasks));
}
}

View file

@ -11,8 +11,9 @@ repository = "https://github.com/sxyazi/yazi"
[dependencies]
yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" }
yazi-config = { path = "../yazi-config", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }
yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" }
yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" }
yazi-shared = { path = "../yazi-shared", version = "0.2.3" }
# External dependencies
anyhow = "^1"

View file

@ -1,9 +1,10 @@
use anyhow::Result;
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::mpsc};
use yazi_plugin::external::{self, ShellOpt};
use yazi_proxy::AppProxy;
use super::ProcessOpOpen;
use crate::{Scheduler, TaskProg, BLOCKER};
use crate::{TaskProg, BLOCKER};
pub struct Process {
prog: mpsc::UnboundedSender<TaskProg>,
@ -16,7 +17,7 @@ impl Process {
let opt = ShellOpt::from(&mut task);
if task.block {
let _guard = BLOCKER.acquire().await.unwrap();
Scheduler::app_stop().await;
AppProxy::stop().await;
match external::shell(opt) {
Ok(mut child) => {
@ -28,7 +29,7 @@ impl Process {
self.fail(task.id, format!("Failed to spawn process: {e}"))?;
}
}
return Ok(Scheduler::app_resume());
return Ok(AppProxy::resume());
}
if task.orphan {

View file

@ -5,7 +5,7 @@ use parking_lot::Mutex;
use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}};
use yazi_config::{open::Opener, plugin::PluginRule, TASKS};
use yazi_plugin::ValueSendable;
use yazi_shared::{emit, event::Cmd, fs::{unique_path, Url}, Layer, Throttle};
use yazi_shared::{fs::{unique_path, Url}, Throttle};
use super::{Running, TaskProg, TaskStage};
use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Preload, PreloadOpRule, PreloadOpSize}, process::{Process, ProcessOpOpen}, TaskKind, TaskOp, HIGH, LOW, NORMAL};
@ -163,16 +163,6 @@ impl Scheduler {
b
}
pub async fn app_stop() {
let (tx, rx) = oneshot::channel::<()>();
emit!(Call(Cmd::new("stop").with_data(tx), Layer::App));
rx.await.ok();
}
pub fn app_resume() {
emit!(Call(Cmd::new("resume"), Layer::App));
}
pub fn file_cut(&self, from: Url, mut to: Url, force: bool) {
let mut running = self.running.lock();
let id = running.add(TaskKind::User, format!("Cut {:?} to {:?}", from, to));