mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
a281c3f81a
commit
a4629adc30
21 changed files with 215 additions and 127 deletions
|
|
@ -64,6 +64,7 @@ impl Keymap {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self, layer: Layer) -> &Vec<Control> {
|
pub fn get(&self, layer: Layer) -> &Vec<Control> {
|
||||||
match layer {
|
match layer {
|
||||||
|
Layer::App => unreachable!(),
|
||||||
Layer::Manager => &self.manager,
|
Layer::Manager => &self.manager,
|
||||||
Layer::Tasks => &self.tasks,
|
Layer::Tasks => &self.tasks,
|
||||||
Layer::Select => &self.select,
|
Layer::Select => &self.select,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
use yazi_config::popup::{Origin, Position};
|
use yazi_config::popup::{Origin, Position};
|
||||||
|
use yazi_shared::{Exec, Layer};
|
||||||
|
|
||||||
use crate::{completion::Completion, help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which};
|
use crate::{completion::Completion, emit, help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which};
|
||||||
|
|
||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
pub manager: Manager,
|
pub manager: Manager,
|
||||||
|
|
@ -26,6 +28,21 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub async fn stop() {
|
||||||
|
let (tx, rx) = oneshot::channel::<()>();
|
||||||
|
emit!(Call(Exec::call("stop", vec!["true".to_string()]).with_data(Some(tx)).vec(), Layer::App));
|
||||||
|
rx.await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn resume() {
|
||||||
|
emit!(Call(
|
||||||
|
Exec::call("stop", vec!["false".to_string()]).with_data(None::<oneshot::Sender<()>>).vec(),
|
||||||
|
Layer::App
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn area(&self, position: &Position) -> Rect {
|
pub fn area(&self, position: &Position) -> Rect {
|
||||||
if position.origin != Origin::Hovered {
|
if position.origin != Origin::Hovered {
|
||||||
return position.rect();
|
return position.rect();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use std::{collections::BTreeMap, ffi::OsString};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
||||||
use yazi_config::open::Opener;
|
|
||||||
use yazi_shared::{fs::Url, term::Term, Exec, Layer, RoCell};
|
use yazi_shared::{fs::Url, term::Term, Exec, Layer, RoCell};
|
||||||
|
|
||||||
use super::files::FilesOp;
|
use super::files::FilesOp;
|
||||||
|
|
@ -16,7 +15,6 @@ pub enum Event {
|
||||||
Paste(String),
|
Paste(String),
|
||||||
Render(String),
|
Render(String),
|
||||||
Resize(u16, u16),
|
Resize(u16, u16),
|
||||||
Stop(bool, Option<oneshot::Sender<()>>),
|
|
||||||
Call(Vec<Exec>, Layer),
|
Call(Vec<Exec>, Layer),
|
||||||
|
|
||||||
// Manager
|
// Manager
|
||||||
|
|
@ -24,9 +22,6 @@ pub enum Event {
|
||||||
Pages(usize),
|
Pages(usize),
|
||||||
Mimetype(BTreeMap<Url, String>),
|
Mimetype(BTreeMap<Url, String>),
|
||||||
Preview(PreviewLock),
|
Preview(PreviewLock),
|
||||||
|
|
||||||
// Tasks
|
|
||||||
Open(Vec<(OsString, String)>, Option<Opener>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
|
|
@ -56,10 +51,6 @@ macro_rules! emit {
|
||||||
(Resize($cols:expr, $rows:expr)) => {
|
(Resize($cols:expr, $rows:expr)) => {
|
||||||
$crate::Event::Resize($cols, $rows).emit();
|
$crate::Event::Resize($cols, $rows).emit();
|
||||||
};
|
};
|
||||||
(Stop($state:expr)) => {{
|
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
|
||||||
$crate::Event::Stop($state, Some(tx)).wait(rx)
|
|
||||||
}};
|
|
||||||
(Call($exec:expr, $layer:expr)) => {
|
(Call($exec:expr, $layer:expr)) => {
|
||||||
$crate::Event::Call($exec, $layer).emit();
|
$crate::Event::Call($exec, $layer).emit();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::ffi::OsString;
|
||||||
use yazi_config::{popup::SelectCfg, OPEN};
|
use yazi_config::{popup::SelectCfg, OPEN};
|
||||||
use yazi_shared::{Exec, MIME_DIR};
|
use yazi_shared::{Exec, MIME_DIR};
|
||||||
|
|
||||||
use crate::{emit, external, manager::Manager, select::Select};
|
use crate::{external, manager::Manager, select::Select, tasks::Tasks};
|
||||||
|
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
interactive: bool,
|
interactive: bool,
|
||||||
|
|
@ -22,7 +22,7 @@ impl Manager {
|
||||||
|
|
||||||
let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect()));
|
let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect()));
|
||||||
if let Ok(choice) = result.await {
|
if let Ok(choice) = result.await {
|
||||||
emit!(Open(files, Some(openers[choice].clone())));
|
Tasks::_open(files, Some(openers[choice].clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl Manager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit!(Open(files, None));
|
Tasks::_open(files, None);
|
||||||
});
|
});
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
||||||
use yazi_config::{popup::InputCfg, OPEN, PREVIEW};
|
use yazi_config::{popup::InputCfg, OPEN, PREVIEW};
|
||||||
use yazi_shared::{fs::{max_common_root, Url}, term::Term, Defer, Exec};
|
use yazi_shared::{fs::{max_common_root, Url}, term::Term, Defer, Exec};
|
||||||
|
|
||||||
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::Input, manager::Manager, Event, BLOCKER};
|
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::Input, manager::Manager, Ctx, BLOCKER};
|
||||||
|
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
force: bool,
|
force: bool,
|
||||||
|
|
@ -86,10 +86,10 @@ impl Manager {
|
||||||
|
|
||||||
let _guard = BLOCKER.acquire().await.unwrap();
|
let _guard = BLOCKER.acquire().await.unwrap();
|
||||||
let _defer = Defer::new(|| {
|
let _defer = Defer::new(|| {
|
||||||
Event::Stop(false, None).emit();
|
Ctx::resume();
|
||||||
tokio::spawn(fs::remove_file(tmp.clone()))
|
tokio::spawn(fs::remove_file(tmp.clone()))
|
||||||
});
|
});
|
||||||
emit!(Stop(true)).await;
|
Ctx::stop().await;
|
||||||
|
|
||||||
let mut child = external::shell(ShellOpt {
|
let mut child = external::shell(ShellOpt {
|
||||||
cmd: (*opener.exec).into(),
|
cmd: (*opener.exec).into(),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use yazi_shared::Exec;
|
use yazi_shared::Exec;
|
||||||
|
|
||||||
use crate::manager::Manager;
|
use crate::{manager::Manager, Ctx};
|
||||||
|
|
||||||
pub struct Opt;
|
pub struct Opt;
|
||||||
impl From<&Exec> for Opt {
|
impl From<&Exec> for Opt {
|
||||||
|
|
@ -11,7 +11,7 @@ impl Manager {
|
||||||
pub fn suspend(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn suspend(&mut self, _: impl Into<Opt>) -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
crate::emit!(Stop(true)).await;
|
Ctx::stop().await;
|
||||||
unsafe { libc::raise(libc::SIGTSTP) };
|
unsafe { libc::raise(libc::SIGTSTP) };
|
||||||
});
|
});
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use yazi_shared::Exec;
|
use yazi_shared::{fs::ends_with_slash, Defer, Exec};
|
||||||
use yazi_shared::{fs::ends_with_slash, Defer};
|
|
||||||
|
|
||||||
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, tab::Tab, Event, BLOCKER};
|
use crate::{external::{self, FzfOpt, ZoxideOpt}, tab::Tab, Ctx, BLOCKER};
|
||||||
|
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
type_: OptType,
|
type_: OptType,
|
||||||
|
|
@ -36,8 +35,8 @@ impl Tab {
|
||||||
let cwd = self.current.cwd.clone();
|
let cwd = self.current.cwd.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _guard = BLOCKER.acquire().await.unwrap();
|
let _guard = BLOCKER.acquire().await.unwrap();
|
||||||
let _defer = Defer::new(|| Event::Stop(false, None).emit());
|
let _defer = Defer::new(Ctx::resume);
|
||||||
emit!(Stop(true)).await;
|
Ctx::stop().await;
|
||||||
|
|
||||||
let result = if opt.type_ == OptType::Fzf {
|
let result = if opt.type_ == OptType::Fzf {
|
||||||
external::fzf(FzfOpt { cwd }).await
|
external::fzf(FzfOpt { cwd }).await
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use yazi_config::{open::Opener, popup::InputCfg};
|
use yazi_config::{open::Opener, popup::InputCfg};
|
||||||
use yazi_shared::Exec;
|
use yazi_shared::Exec;
|
||||||
|
|
||||||
use crate::{emit, input::Input, tab::Tab};
|
use crate::{input::Input, tab::Tab, tasks::Tasks};
|
||||||
|
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
cmd: String,
|
cmd: String,
|
||||||
|
|
@ -37,7 +37,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit!(Open(
|
Tasks::_open(
|
||||||
selected,
|
selected,
|
||||||
Some(Opener {
|
Some(Opener {
|
||||||
exec: opt.cmd,
|
exec: opt.cmd,
|
||||||
|
|
@ -46,8 +46,8 @@ impl Tab {
|
||||||
desc: Default::default(),
|
desc: Default::default(),
|
||||||
for_: None,
|
for_: None,
|
||||||
spread: true,
|
spread: true,
|
||||||
})
|
}),
|
||||||
));
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@ use std::io::{stdout, Write};
|
||||||
|
|
||||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||||
use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time};
|
use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time};
|
||||||
use yazi_shared::Exec;
|
use yazi_shared::{term::Term, Defer, Exec};
|
||||||
use yazi_shared::{term::Term, Defer};
|
|
||||||
|
|
||||||
use crate::{emit, tasks::Tasks, Event, BLOCKER};
|
use crate::{tasks::Tasks, Ctx, BLOCKER};
|
||||||
|
|
||||||
pub struct Opt;
|
pub struct Opt;
|
||||||
|
|
||||||
|
|
@ -32,10 +31,10 @@ impl Tasks {
|
||||||
task.logs.clone()
|
task.logs.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
emit!(Stop(true)).await;
|
Ctx::stop().await;
|
||||||
let _defer = Defer::new(|| {
|
let _defer = Defer::new(|| {
|
||||||
disable_raw_mode().ok();
|
disable_raw_mode().ok();
|
||||||
Event::Stop(false, None).emit();
|
Ctx::resume();
|
||||||
});
|
});
|
||||||
|
|
||||||
Term::clear(&mut stdout()).ok();
|
Term::clear(&mut stdout()).ok();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod arrow;
|
mod arrow;
|
||||||
mod cancel;
|
mod cancel;
|
||||||
mod inspect;
|
mod inspect;
|
||||||
|
mod open;
|
||||||
mod toggle;
|
mod toggle;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
|
||||||
51
yazi-core/src/tasks/commands/open.rs
Normal file
51
yazi-core/src/tasks/commands/open.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use std::ffi::OsString;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use yazi_config::{open::Opener, BOOT};
|
||||||
|
use yazi_shared::{Exec, Layer};
|
||||||
|
|
||||||
|
use crate::{emit, tasks::Tasks};
|
||||||
|
|
||||||
|
pub struct Opt {
|
||||||
|
targets: Vec<(OsString, String)>,
|
||||||
|
opener: Option<Opener>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Exec> for Opt {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(e: &Exec) -> Result<Self, Self::Error> {
|
||||||
|
e.take_data().ok_or_else(|| anyhow!("invalid data"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tasks {
|
||||||
|
pub fn _open(targets: Vec<(OsString, String)>, opener: Option<Opener>) {
|
||||||
|
emit!(Call(Exec::call("open", vec![]).with_data(Opt { targets, opener }).vec(), Layer::Tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(&mut self, opt: impl TryInto<Opt>) -> bool {
|
||||||
|
let Ok(opt) = opt.try_into() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(p) = &BOOT.chooser_file {
|
||||||
|
let paths = opt.targets.into_iter().fold(OsString::new(), |mut s, (p, _)| {
|
||||||
|
s.push(p);
|
||||||
|
s.push("\n");
|
||||||
|
s
|
||||||
|
});
|
||||||
|
|
||||||
|
std::fs::write(p, paths.as_encoded_bytes()).ok();
|
||||||
|
emit!(Quit(false));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(opener) = opt.opener {
|
||||||
|
self.file_open_with(&opener, &opt.targets.into_iter().map(|(f, _)| f).collect::<Vec<_>>());
|
||||||
|
} else {
|
||||||
|
self.file_open(&opt.targets);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{ffi::OsString, mem};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::{mpsc, oneshot}};
|
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::{mpsc, oneshot}};
|
||||||
|
|
||||||
use crate::{emit, external::{self, ShellOpt}, tasks::TaskOp, BLOCKER};
|
use crate::{external::{self, ShellOpt}, tasks::TaskOp, Ctx, BLOCKER};
|
||||||
|
|
||||||
pub(crate) struct Process {
|
pub(crate) struct Process {
|
||||||
sch: mpsc::UnboundedSender<TaskOp>,
|
sch: mpsc::UnboundedSender<TaskOp>,
|
||||||
|
|
@ -37,7 +37,7 @@ impl Process {
|
||||||
let opt = ShellOpt::from(&mut task);
|
let opt = ShellOpt::from(&mut task);
|
||||||
if task.block {
|
if task.block {
|
||||||
let _guard = BLOCKER.acquire().await.unwrap();
|
let _guard = BLOCKER.acquire().await.unwrap();
|
||||||
emit!(Stop(true)).await;
|
Ctx::stop().await;
|
||||||
|
|
||||||
match external::shell(opt) {
|
match external::shell(opt) {
|
||||||
Ok(mut child) => {
|
Ok(mut child) => {
|
||||||
|
|
@ -49,7 +49,7 @@ impl Process {
|
||||||
self.fail(task.id, format!("Failed to spawn process: {e}"))?;
|
self.fail(task.id, format!("Failed to spawn process: {e}"))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(emit!(Stop(false)).await);
|
return Ok(Ctx::resume());
|
||||||
}
|
}
|
||||||
|
|
||||||
if task.orphan {
|
if task.orphan {
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
use std::{ffi::OsString, sync::atomic::Ordering};
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
use ratatui::{backend::Backend, prelude::Rect};
|
use ratatui::{backend::Backend, prelude::Rect};
|
||||||
use tokio::sync::oneshot;
|
|
||||||
use yazi_config::{keymap::Key, BOOT};
|
use yazi_config::{keymap::Key, BOOT};
|
||||||
use yazi_core::{emit, files::FilesOp, input::InputMode, manager::Manager, preview::COLLISION, Ctx, Event};
|
use yazi_core::{emit, files::FilesOp, input::InputMode, preview::COLLISION, Ctx, Event};
|
||||||
use yazi_shared::{term::Term, Exec, Layer};
|
use yazi_shared::{term::Term, Exec, Layer};
|
||||||
|
|
||||||
use crate::{Executor, Logs, Panic, Root, Signals};
|
use crate::{Executor, Logs, Panic, Root, Signals};
|
||||||
|
|
||||||
pub(super) struct App {
|
pub(crate) struct App {
|
||||||
cx: Ctx,
|
pub(crate) cx: Ctx,
|
||||||
term: Option<Term>,
|
pub(crate) term: Option<Term>,
|
||||||
signals: Signals,
|
pub(crate) signals: Signals,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub(super) async fn run() -> Result<()> {
|
pub(crate) async fn run() -> Result<()> {
|
||||||
Panic::install();
|
Panic::install();
|
||||||
let _log = Logs::init()?;
|
let _log = Logs::init()?;
|
||||||
let term = Term::start()?;
|
let term = Term::start()?;
|
||||||
|
|
@ -35,7 +34,6 @@ impl App {
|
||||||
Event::Paste(str) => app.dispatch_paste(str),
|
Event::Paste(str) => app.dispatch_paste(str),
|
||||||
Event::Render(_) => app.dispatch_render()?,
|
Event::Render(_) => app.dispatch_render()?,
|
||||||
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows),
|
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows),
|
||||||
Event::Stop(state, tx) => app.dispatch_stop(state, tx),
|
|
||||||
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
|
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
|
||||||
event => app.dispatch_module(event),
|
event => app.dispatch_module(event),
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +51,7 @@ impl App {
|
||||||
|
|
||||||
fn dispatch_key(&mut self, key: KeyEvent) {
|
fn dispatch_key(&mut self, key: KeyEvent) {
|
||||||
let key = Key::from(key);
|
let key = Key::from(key);
|
||||||
if Executor::new(&mut self.cx).handle(key) {
|
if Executor::new(self).handle(key) {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,25 +119,9 @@ impl App {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_stop(&mut self, state: bool, tx: Option<oneshot::Sender<()>>) {
|
|
||||||
self.cx.manager.active_mut().preview.reset_image();
|
|
||||||
if state {
|
|
||||||
self.signals.stop_term(true);
|
|
||||||
self.term = None;
|
|
||||||
} else {
|
|
||||||
self.term = Some(Term::start().unwrap());
|
|
||||||
self.signals.stop_term(false);
|
|
||||||
emit!(Render);
|
|
||||||
Manager::_hover(None);
|
|
||||||
}
|
|
||||||
if let Some(tx) = tx {
|
|
||||||
tx.send(()).ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn dispatch_call(&mut self, exec: Vec<Exec>, layer: Layer) {
|
fn dispatch_call(&mut self, exec: Vec<Exec>, layer: Layer) {
|
||||||
if Executor::new(&mut self.cx).dispatch(&exec, layer) {
|
if Executor::new(self).dispatch(&exec, layer) {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -176,26 +158,6 @@ impl App {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Open(targets, opener) => {
|
|
||||||
if let Some(p) = &BOOT.chooser_file {
|
|
||||||
let paths = targets.into_iter().fold(OsString::new(), |mut s, (p, _)| {
|
|
||||||
s.push(p);
|
|
||||||
s.push("\n");
|
|
||||||
s
|
|
||||||
});
|
|
||||||
|
|
||||||
std::fs::write(p, paths.as_encoded_bytes()).ok();
|
|
||||||
return emit!(Quit(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(opener) = opener {
|
|
||||||
tasks.file_open_with(&opener, &targets.into_iter().map(|(f, _)| f).collect::<Vec<_>>());
|
|
||||||
} else {
|
|
||||||
tasks.file_open(&targets);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
yazi-fm/src/app/commands/mod.rs
Normal file
1
yazi-fm/src/app/commands/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
mod stop;
|
||||||
42
yazi-fm/src/app/commands/stop.rs
Normal file
42
yazi-fm/src/app/commands/stop.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
use yazi_core::{emit, manager::Manager};
|
||||||
|
use yazi_shared::{term::Term, Exec};
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
|
pub struct Opt {
|
||||||
|
state: bool,
|
||||||
|
tx: Option<oneshot::Sender<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Exec> for Opt {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(e: &Exec) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self { state: e.args.first().map_or(false, |s| s == "true"), tx: e.take_data() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub(crate) fn stop(&mut self, opt: impl TryInto<Opt>) -> bool {
|
||||||
|
let Ok(opt) = opt.try_into() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cx.manager.active_mut().preview.reset_image();
|
||||||
|
if opt.state {
|
||||||
|
self.signals.stop_term(true);
|
||||||
|
self.term = None;
|
||||||
|
} else {
|
||||||
|
self.term = Some(Term::start().unwrap());
|
||||||
|
self.signals.stop_term(false);
|
||||||
|
emit!(Render);
|
||||||
|
Manager::_hover(None);
|
||||||
|
}
|
||||||
|
if let Some(tx) = opt.tx {
|
||||||
|
tx.send(()).ok();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
4
yazi-fm/src/app/mod.rs
Normal file
4
yazi-fm/src/app/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
mod app;
|
||||||
|
mod commands;
|
||||||
|
|
||||||
|
pub(crate) use app::*;
|
||||||
|
|
@ -1,35 +1,39 @@
|
||||||
use yazi_config::{keymap::{Control, Key}, KEYMAP};
|
use yazi_config::{keymap::{Control, Key}, KEYMAP};
|
||||||
use yazi_core::{input::InputMode, Ctx};
|
use yazi_core::input::InputMode;
|
||||||
use yazi_shared::{Exec, Layer};
|
use yazi_shared::{Exec, Layer};
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
|
||||||
pub(super) struct Executor<'a> {
|
pub(super) struct Executor<'a> {
|
||||||
cx: &'a mut Ctx,
|
app: &'a mut App,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Executor<'a> {
|
impl<'a> Executor<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn new(cx: &'a mut Ctx) -> Self { Self { cx } }
|
pub(super) fn new(app: &'a mut App) -> Self { Self { app } }
|
||||||
|
|
||||||
pub(super) fn handle(&mut self, key: Key) -> bool {
|
pub(super) fn handle(&mut self, key: Key) -> bool {
|
||||||
if self.cx.which.visible {
|
let cx = &mut self.app.cx;
|
||||||
return self.cx.which.press(key);
|
|
||||||
|
if cx.which.visible {
|
||||||
|
return cx.which.press(key);
|
||||||
}
|
}
|
||||||
if self.cx.help.visible && self.cx.help.type_(&key) {
|
if cx.help.visible && cx.help.type_(&key) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if self.cx.input.visible && self.cx.input.type_(&key) {
|
if cx.input.visible && cx.input.type_(&key) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = if self.cx.completion.visible {
|
let b = if cx.completion.visible {
|
||||||
self.matches(Layer::Completion, key).or_else(|| self.matches(Layer::Input, key))
|
self.matches(Layer::Completion, key).or_else(|| self.matches(Layer::Input, key))
|
||||||
} else if self.cx.help.visible {
|
} else if cx.help.visible {
|
||||||
self.matches(Layer::Help, key)
|
self.matches(Layer::Help, key)
|
||||||
} else if self.cx.input.visible {
|
} else if cx.input.visible {
|
||||||
self.matches(Layer::Input, key)
|
self.matches(Layer::Input, key)
|
||||||
} else if self.cx.select.visible {
|
} else if cx.select.visible {
|
||||||
self.matches(Layer::Select, key)
|
self.matches(Layer::Select, key)
|
||||||
} else if self.cx.tasks.visible {
|
} else if cx.tasks.visible {
|
||||||
self.matches(Layer::Tasks, key)
|
self.matches(Layer::Tasks, key)
|
||||||
} else {
|
} else {
|
||||||
self.matches(Layer::Manager, key)
|
self.matches(Layer::Manager, key)
|
||||||
|
|
@ -45,7 +49,7 @@ impl<'a> Executor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(if on.len() > 1 {
|
return Some(if on.len() > 1 {
|
||||||
self.cx.which.show(&key, layer)
|
self.app.cx.which.show(&key, layer)
|
||||||
} else {
|
} else {
|
||||||
self.dispatch(exec, layer)
|
self.dispatch(exec, layer)
|
||||||
});
|
});
|
||||||
|
|
@ -58,6 +62,7 @@ impl<'a> Executor<'a> {
|
||||||
let mut render = false;
|
let mut render = false;
|
||||||
for e in exec {
|
for e in exec {
|
||||||
render |= match layer {
|
render |= match layer {
|
||||||
|
Layer::App => self.app(e),
|
||||||
Layer::Manager => self.manager(e),
|
Layer::Manager => self.manager(e),
|
||||||
Layer::Tasks => self.tasks(e),
|
Layer::Tasks => self.tasks(e),
|
||||||
Layer::Select => self.select(e),
|
Layer::Select => self.select(e),
|
||||||
|
|
@ -70,21 +75,35 @@ impl<'a> Executor<'a> {
|
||||||
render
|
render
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn app(&mut self, exec: &Exec) -> bool {
|
||||||
|
macro_rules! on {
|
||||||
|
($name:ident) => {
|
||||||
|
if exec.cmd == stringify!($name) {
|
||||||
|
return self.app.$name(exec);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
on!(stop);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn manager(&mut self, exec: &Exec) -> bool {
|
fn manager(&mut self, exec: &Exec) -> bool {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
(MANAGER, $name:ident $(,$args:expr)*) => {
|
(MANAGER, $name:ident $(,$args:expr)*) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.manager.$name(exec, $($args),*);
|
return self.app.cx.manager.$name(exec, $($args),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(ACTIVE, $name:ident) => {
|
(ACTIVE, $name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.manager.active_mut().$name(exec);
|
return self.app.cx.manager.active_mut().$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(TABS, $name:ident) => {
|
(TABS, $name:ident) => {
|
||||||
if exec.cmd == concat!("tab_", stringify!($name)) {
|
if exec.cmd == concat!("tab_", stringify!($name)) {
|
||||||
return self.cx.manager.tabs.$name(exec);
|
return self.app.cx.manager.tabs.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -92,8 +111,8 @@ impl<'a> Executor<'a> {
|
||||||
on!(MANAGER, peek);
|
on!(MANAGER, peek);
|
||||||
on!(MANAGER, hover);
|
on!(MANAGER, hover);
|
||||||
on!(MANAGER, refresh);
|
on!(MANAGER, refresh);
|
||||||
on!(MANAGER, quit, &self.cx.tasks);
|
on!(MANAGER, quit, &self.app.cx.tasks);
|
||||||
on!(MANAGER, close, &self.cx.tasks);
|
on!(MANAGER, close, &self.app.cx.tasks);
|
||||||
on!(MANAGER, suspend);
|
on!(MANAGER, suspend);
|
||||||
on!(ACTIVE, escape);
|
on!(ACTIVE, escape);
|
||||||
|
|
||||||
|
|
@ -114,9 +133,9 @@ impl<'a> Executor<'a> {
|
||||||
// Operation
|
// Operation
|
||||||
on!(MANAGER, open);
|
on!(MANAGER, open);
|
||||||
on!(MANAGER, yank);
|
on!(MANAGER, yank);
|
||||||
on!(MANAGER, paste, &self.cx.tasks);
|
on!(MANAGER, paste, &self.app.cx.tasks);
|
||||||
on!(MANAGER, link, &self.cx.tasks);
|
on!(MANAGER, link, &self.app.cx.tasks);
|
||||||
on!(MANAGER, remove, &self.cx.tasks);
|
on!(MANAGER, remove, &self.app.cx.tasks);
|
||||||
on!(MANAGER, create);
|
on!(MANAGER, create);
|
||||||
on!(MANAGER, rename);
|
on!(MANAGER, rename);
|
||||||
on!(ACTIVE, copy);
|
on!(ACTIVE, copy);
|
||||||
|
|
@ -142,9 +161,9 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
match exec.cmd.as_bytes() {
|
match exec.cmd.as_bytes() {
|
||||||
// Tasks
|
// Tasks
|
||||||
b"tasks_show" => self.cx.tasks.toggle(()),
|
b"tasks_show" => self.app.cx.tasks.toggle(()),
|
||||||
// Help
|
// Help
|
||||||
b"help" => self.cx.help.toggle(Layer::Manager),
|
b"help" => self.app.cx.help.toggle(Layer::Manager),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,24 +172,25 @@ impl<'a> Executor<'a> {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.tasks.$name(exec);
|
return self.app.cx.tasks.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:ident, $alias:literal) => {
|
($name:ident, $alias:literal) => {
|
||||||
if exec.cmd == $alias {
|
if exec.cmd == $alias {
|
||||||
return self.cx.tasks.$name(exec);
|
return self.app.cx.tasks.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
on!(update);
|
on!(update);
|
||||||
|
on!(open);
|
||||||
on!(toggle, "close");
|
on!(toggle, "close");
|
||||||
on!(arrow);
|
on!(arrow);
|
||||||
on!(inspect);
|
on!(inspect);
|
||||||
on!(cancel);
|
on!(cancel);
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.cx.help.toggle(Layer::Tasks),
|
"help" => self.app.cx.help.toggle(Layer::Tasks),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +199,7 @@ impl<'a> Executor<'a> {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.select.$name(exec);
|
return self.app.cx.select.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +209,7 @@ impl<'a> Executor<'a> {
|
||||||
on!(arrow);
|
on!(arrow);
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.cx.help.toggle(Layer::Select),
|
"help" => self.app.cx.help.toggle(Layer::Select),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,12 +218,12 @@ impl<'a> Executor<'a> {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.input.$name(exec);
|
return self.app.cx.input.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:ident, $alias:literal) => {
|
($name:ident, $alias:literal) => {
|
||||||
if exec.cmd == $alias {
|
if exec.cmd == $alias {
|
||||||
return self.cx.input.$name(exec);
|
return self.app.cx.input.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -217,13 +237,13 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
if exec.cmd.as_str() == "complete" {
|
if exec.cmd.as_str() == "complete" {
|
||||||
return if exec.named.contains_key("trigger") {
|
return if exec.named.contains_key("trigger") {
|
||||||
self.cx.completion.trigger(exec)
|
self.app.cx.completion.trigger(exec)
|
||||||
} else {
|
} else {
|
||||||
self.cx.input.complete(exec)
|
self.app.cx.input.complete(exec)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.cx.input.mode() {
|
match self.app.cx.input.mode() {
|
||||||
InputMode::Normal => {
|
InputMode::Normal => {
|
||||||
on!(insert);
|
on!(insert);
|
||||||
on!(visual);
|
on!(visual);
|
||||||
|
|
@ -236,7 +256,7 @@ impl<'a> Executor<'a> {
|
||||||
on!(redo);
|
on!(redo);
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.cx.help.toggle(Layer::Input),
|
"help" => self.app.cx.help.toggle(Layer::Input),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +273,7 @@ impl<'a> Executor<'a> {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.help.$name(exec);
|
return self.app.cx.help.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +283,7 @@ impl<'a> Executor<'a> {
|
||||||
on!(filter);
|
on!(filter);
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"close" => self.cx.help.toggle(Layer::Help),
|
"close" => self.app.cx.help.toggle(Layer::Help),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +292,7 @@ impl<'a> Executor<'a> {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if exec.cmd == stringify!($name) {
|
if exec.cmd == stringify!($name) {
|
||||||
return self.cx.completion.$name(exec);
|
return self.app.cx.completion.$name(exec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +303,7 @@ impl<'a> Executor<'a> {
|
||||||
on!(arrow);
|
on!(arrow);
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
match exec.cmd.as_str() {
|
||||||
"help" => self.cx.help.toggle(Layer::Completion),
|
"help" => self.app.cx.help.toggle(Layer::Completion),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ mod tasks;
|
||||||
mod which;
|
mod which;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
use app::*;
|
|
||||||
use executor::*;
|
use executor::*;
|
||||||
use logs::*;
|
use logs::*;
|
||||||
use panic::*;
|
use panic::*;
|
||||||
|
|
@ -34,5 +33,5 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
yazi_adaptor::init();
|
yazi_adaptor::init();
|
||||||
|
|
||||||
App::run().await
|
app::App::run().await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ impl Panic {
|
||||||
|
|
||||||
let hook = std::panic::take_hook();
|
let hook = std::panic::take_hook();
|
||||||
std::panic::set_hook(Box::new(move |info| {
|
std::panic::set_hook(Box::new(move |info| {
|
||||||
_ = Term::goodbye(|| {
|
Term::goodbye(|| {
|
||||||
hook(info);
|
hook(info);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ impl Signals {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn spawn_system_task(&self) -> Result<JoinHandle<()>> {
|
fn spawn_system_task(&self) -> Result<JoinHandle<()>> {
|
||||||
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM};
|
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM};
|
||||||
|
use yazi_core::Ctx;
|
||||||
|
|
||||||
let tx = self.tx.clone();
|
let tx = self.tx.clone();
|
||||||
let mut signals = signal_hook_tokio::Signals::new([
|
let mut signals = signal_hook_tokio::Signals::new([
|
||||||
|
|
@ -67,9 +68,7 @@ impl Signals {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SIGCONT => {
|
SIGCONT => Ctx::resume(),
|
||||||
tx.send(Event::Stop(false, None)).ok();
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::fmt::{self, Display};
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
pub enum Layer {
|
pub enum Layer {
|
||||||
#[default]
|
#[default]
|
||||||
|
App,
|
||||||
Manager,
|
Manager,
|
||||||
Tasks,
|
Tasks,
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -15,6 +16,7 @@ pub enum Layer {
|
||||||
impl Display for Layer {
|
impl Display for Layer {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Self::App => write!(f, "app"),
|
||||||
Self::Manager => write!(f, "manager"),
|
Self::Manager => write!(f, "manager"),
|
||||||
Self::Tasks => write!(f, "tasks"),
|
Self::Tasks => write!(f, "tasks"),
|
||||||
Self::Select => write!(f, "select"),
|
Self::Select => write!(f, "select"),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue