mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
added option to pass opened file to stdout
Signed-off-by: aserowy <serowy@hotmail.com>
This commit is contained in:
parent
93c8d90a51
commit
d78eafb37c
10 changed files with 159 additions and 58 deletions
|
|
@ -11,10 +11,13 @@ pub struct Args {
|
|||
|
||||
/// Write the cwd on exit to this file
|
||||
#[arg(long)]
|
||||
pub cwd_file: Option<PathBuf>,
|
||||
pub cwd_file: Option<PathBuf>,
|
||||
/// Write the selected files on open emitted by the chooser mode
|
||||
#[arg(long)]
|
||||
pub chooser_file: Option<PathBuf>,
|
||||
/// Write the selected files on open to stdout
|
||||
#[arg(long, action)]
|
||||
pub chooser_stdout: bool,
|
||||
|
||||
/// Clear the cache directory
|
||||
#[arg(long, action)]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use tokio::fs;
|
||||
use tracing::error;
|
||||
use yazi_config::{popup::SelectCfg, ARGS, OPEN};
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_shared::{emit, event::Exec, fs::{File, Url}, Layer, MIME_DIR};
|
||||
use yazi_shared::event::QuitAction;
|
||||
use yazi_shared::{
|
||||
emit,
|
||||
event::Exec,
|
||||
fs::{File, Url},
|
||||
Layer, MIME_DIR,
|
||||
};
|
||||
|
||||
use crate::{manager::Manager, select::Select, tasks::Tasks};
|
||||
|
||||
pub struct Opt {
|
||||
targets: Option<Vec<(Url, Option<String>)>>,
|
||||
targets: Option<Vec<(Url, Option<String>)>>,
|
||||
interactive: bool,
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +29,7 @@ impl Manager {
|
|||
let selected = self.selected();
|
||||
if selected.is_empty() {
|
||||
return;
|
||||
} else if Self::quit_with_selected(&selected) {
|
||||
} else if Self::quit_with_chooser(&selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -95,10 +100,9 @@ impl Manager {
|
|||
});
|
||||
}
|
||||
|
||||
fn quit_with_selected(selected: &[&File]) -> bool {
|
||||
let Some(p) = ARGS.chooser_file.clone() else {
|
||||
return false;
|
||||
};
|
||||
fn quit_with_chooser(selected: &[&File]) -> bool {
|
||||
let mut quit_actions = vec![QuitAction::CwdToFile];
|
||||
let mut initiated_quit = false;
|
||||
|
||||
let paths = selected.iter().fold(OsString::new(), |mut s, &f| {
|
||||
s.push(f.url.as_os_str());
|
||||
|
|
@ -106,10 +110,20 @@ impl Manager {
|
|||
s
|
||||
});
|
||||
|
||||
if ARGS.chooser_file.is_some() {
|
||||
quit_actions.push(QuitAction::SelectToFile(paths.clone()));
|
||||
initiated_quit = true;
|
||||
};
|
||||
|
||||
if ARGS.chooser_stdout {
|
||||
quit_actions.push(QuitAction::SelectToStdout(paths.clone()));
|
||||
initiated_quit = true;
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
fs::write(p, paths.as_encoded_bytes()).await.ok();
|
||||
emit!(Quit(false));
|
||||
emit!(Quit(quit_actions));
|
||||
});
|
||||
true
|
||||
|
||||
initiated_quit
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use yazi_config::popup::InputCfg;
|
||||
use yazi_shared::{emit, event::Exec};
|
||||
use yazi_shared::{
|
||||
emit,
|
||||
event::{Exec, QuitAction},
|
||||
};
|
||||
|
||||
use crate::{input::Input, manager::Manager, tasks::Tasks};
|
||||
|
||||
|
|
@ -8,19 +11,26 @@ pub struct Opt {
|
|||
no_cwd_file: bool,
|
||||
}
|
||||
impl From<()> for Opt {
|
||||
fn from(_: ()) -> Self { Self::default() }
|
||||
fn from(_: ()) -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl From<&Exec> for Opt {
|
||||
fn from(e: &Exec) -> Self { Self { no_cwd_file: e.named.contains_key("no-cwd-file") } }
|
||||
fn from(e: &Exec) -> Self {
|
||||
Self { no_cwd_file: e.named.contains_key("no-cwd-file") }
|
||||
}
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn quit(&self, opt: impl Into<Opt>, tasks: &Tasks) {
|
||||
let opt = opt.into() as Opt;
|
||||
|
||||
let quit_actions =
|
||||
if opt.no_cwd_file { vec![QuitAction::None] } else { vec![QuitAction::CwdToFile] };
|
||||
|
||||
let tasks = tasks.len();
|
||||
if tasks == 0 {
|
||||
emit!(Quit(opt.no_cwd_file));
|
||||
emit!(Quit(quit_actions));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +38,7 @@ impl Manager {
|
|||
let mut result = Input::_show(InputCfg::quit(tasks));
|
||||
if let Some(Ok(choice)) = result.recv().await {
|
||||
if choice == "y" || choice == "Y" {
|
||||
emit!(Quit(opt.no_cwd_file));
|
||||
emit!(Quit(quit_actions));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ impl Select {
|
|||
pub async fn _show(cfg: SelectCfg) -> Result<usize> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
emit!(Call(Exec::call("show", vec![]).with_data(Opt { cfg, tx }).vec(), Layer::Select));
|
||||
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
|
||||
rx.await.unwrap_or_else(|_| Term::goodbye(|| false, None))
|
||||
}
|
||||
|
||||
pub fn show(&mut self, opt: impl TryInto<Opt>) {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,18 @@ use anyhow::{Ok, Result};
|
|||
use crossterm::event::KeyEvent;
|
||||
use yazi_config::keymap::Key;
|
||||
use yazi_core::input::InputMode;
|
||||
use yazi_shared::{emit, event::{Event, Exec, NEED_RENDER}, term::Term, Layer};
|
||||
use yazi_shared::{
|
||||
emit,
|
||||
event::{Event, Exec, NEED_RENDER},
|
||||
term::Term,
|
||||
Layer,
|
||||
};
|
||||
|
||||
use crate::{lives::Lives, Ctx, Executor, Logs, Panic, Signals};
|
||||
|
||||
pub(crate) struct App {
|
||||
pub(crate) cx: Ctx,
|
||||
pub(crate) term: Option<Term>,
|
||||
pub(crate) cx: Ctx,
|
||||
pub(crate) term: Option<Term>,
|
||||
pub(crate) signals: Signals,
|
||||
}
|
||||
|
||||
|
|
@ -35,8 +40,8 @@ impl App {
|
|||
Event::Key(key) => app.dispatch_key(key),
|
||||
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows)?,
|
||||
Event::Paste(str) => app.dispatch_paste(str),
|
||||
Event::Quit(no_cwd_file) => {
|
||||
app.quit(no_cwd_file)?;
|
||||
Event::Quit(quit_actions) => {
|
||||
app.quit(quit_actions)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +59,9 @@ impl App {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn dispatch_key(&mut self, key: KeyEvent) { Executor::new(self).handle(Key::from(key)); }
|
||||
fn dispatch_key(&mut self, key: KeyEvent) {
|
||||
Executor::new(self).handle(Key::from(key));
|
||||
}
|
||||
|
||||
fn dispatch_paste(&mut self, str: String) {
|
||||
if self.cx.input.visible {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,42 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::Result;
|
||||
use yazi_config::ARGS;
|
||||
use yazi_shared::term::Term;
|
||||
use yazi_shared::{event::QuitAction, term::Term};
|
||||
|
||||
use crate::app::App;
|
||||
|
||||
pub struct Opt {
|
||||
no_cwd_file: bool,
|
||||
}
|
||||
|
||||
impl From<bool> for Opt {
|
||||
fn from(no_cwd_file: bool) -> Self { Self { no_cwd_file } }
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub(crate) fn quit(&mut self, opt: impl Into<Opt>) -> Result<()> {
|
||||
let opt = opt.into() as Opt;
|
||||
pub(crate) fn quit(&mut self, quit_actions: Vec<QuitAction>) -> Result<()> {
|
||||
if quit_actions.contains(&QuitAction::None) {
|
||||
Term::goodbye(|| false, None)
|
||||
}
|
||||
|
||||
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !opt.no_cwd_file) {
|
||||
let mut stdout = None;
|
||||
for quit_action in quit_actions {
|
||||
match quit_action {
|
||||
QuitAction::None => unreachable!(),
|
||||
QuitAction::CwdToFile => self.cwd_to_file(),
|
||||
QuitAction::SelectToFile(selected) => self.select_to_file(selected),
|
||||
QuitAction::SelectToStdout(selected) => {
|
||||
stdout = Some(selected.as_encoded_bytes().to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Term::goodbye(|| false, stdout.as_deref());
|
||||
}
|
||||
|
||||
fn cwd_to_file(&self) {
|
||||
if let Some(p) = ARGS.cwd_file.as_ref() {
|
||||
let cwd = self.cx.manager.cwd().as_os_str();
|
||||
std::fs::write(p, cwd.as_encoded_bytes()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
Term::goodbye(|| false);
|
||||
fn select_to_file(&self, selected: OsString) {
|
||||
if let Some(p) = ARGS.chooser_file.clone() {
|
||||
std::fs::write(p, selected.as_encoded_bytes()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@ impl Panic {
|
|||
|
||||
let hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
Term::goodbye(|| {
|
||||
hook(info);
|
||||
true
|
||||
});
|
||||
Term::goodbye(
|
||||
|| {
|
||||
hook(info);
|
||||
true
|
||||
},
|
||||
None,
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
use anyhow::Result;
|
||||
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
|
||||
use futures::StreamExt;
|
||||
use tokio::{select, sync::{mpsc::{self, UnboundedReceiver, UnboundedSender}, oneshot}, task::JoinHandle};
|
||||
use tokio::{
|
||||
select,
|
||||
sync::{
|
||||
mpsc::{self, UnboundedReceiver, UnboundedSender},
|
||||
oneshot,
|
||||
},
|
||||
task::JoinHandle,
|
||||
};
|
||||
use yazi_shared::event::Event;
|
||||
|
||||
pub(super) struct Signals {
|
||||
tx: UnboundedSender<Event>,
|
||||
tx: UnboundedSender<Event>,
|
||||
pub(super) rx: UnboundedReceiver<Event>,
|
||||
|
||||
term_stop_tx: Option<oneshot::Sender<()>>,
|
||||
|
|
@ -42,12 +49,15 @@ impl Signals {
|
|||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn spawn_system_task(&self) -> Result<()> { Ok(()) }
|
||||
fn spawn_system_task(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn spawn_system_task(&self) -> Result<JoinHandle<()>> {
|
||||
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM};
|
||||
use yazi_scheduler::Scheduler;
|
||||
use yazi_shared::event::QuitAction;
|
||||
|
||||
let tx = self.tx.clone();
|
||||
let mut signals = signal_hook_tokio::Signals::new([
|
||||
|
|
@ -61,7 +71,7 @@ impl Signals {
|
|||
while let Some(signal) = signals.next().await {
|
||||
match signal {
|
||||
SIGHUP | SIGTERM | SIGQUIT | SIGINT => {
|
||||
if tx.send(Event::Quit(false)).is_err() {
|
||||
if tx.send(Event::Quit(vec![QuitAction::CwdToFile])).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use crossterm::event::KeyEvent;
|
||||
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
||||
|
||||
|
|
@ -13,31 +15,42 @@ pub enum Event {
|
|||
Key(KeyEvent),
|
||||
Resize(u16, u16),
|
||||
Paste(String),
|
||||
Quit(bool), // no-cwd-file
|
||||
Quit(Vec<QuitAction>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum QuitAction {
|
||||
None,
|
||||
CwdToFile,
|
||||
SelectToFile(OsString),
|
||||
SelectToStdout(OsString),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
#[inline]
|
||||
pub fn init(tx: UnboundedSender<Event>) { TX.init(tx); }
|
||||
pub fn init(tx: UnboundedSender<Event>) {
|
||||
TX.init(tx);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn emit(self) { TX.send(self).ok(); }
|
||||
pub fn emit(self) {
|
||||
TX.send(self).ok();
|
||||
}
|
||||
|
||||
pub async fn wait<T>(self, rx: oneshot::Receiver<T>) -> T {
|
||||
TX.send(self).ok();
|
||||
rx.await.unwrap_or_else(|_| Term::goodbye(|| false))
|
||||
rx.await.unwrap_or_else(|_| Term::goodbye(|| false, None))
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! emit {
|
||||
(Quit($no_cwd_file:expr)) => {
|
||||
$crate::event::Event::Quit($no_cwd_file).emit();
|
||||
(Quit($quit_actions:expr)) => {
|
||||
$crate::event::Event::Quit($quit_actions).emit();
|
||||
};
|
||||
(Call($exec:expr, $layer:expr)) => {
|
||||
$crate::event::Event::Call($exec, $layer).emit();
|
||||
};
|
||||
|
||||
($event:ident) => {
|
||||
$crate::event::Event::$event.emit();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,21 @@
|
|||
use std::{io::{stdout, Stdout, Write}, mem, ops::{Deref, DerefMut}};
|
||||
use std::{
|
||||
io::{stdout, Stdout, Write},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::{event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, terminal::{disable_raw_mode, enable_raw_mode, supports_keyboard_enhancement, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, WindowSize}};
|
||||
use crossterm::{
|
||||
event::{
|
||||
DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange,
|
||||
KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
|
||||
},
|
||||
execute, queue,
|
||||
terminal::{
|
||||
disable_raw_mode, enable_raw_mode, supports_keyboard_enhancement, Clear, ClearType,
|
||||
EnterAlternateScreen, LeaveAlternateScreen, WindowSize,
|
||||
},
|
||||
};
|
||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
pub struct Term {
|
||||
|
|
@ -49,7 +63,7 @@ impl Term {
|
|||
Ok(disable_raw_mode()?)
|
||||
}
|
||||
|
||||
pub fn goodbye(f: impl FnOnce() -> bool) -> ! {
|
||||
pub fn goodbye(f: impl FnOnce() -> bool, last_words: Option<&[u8]>) -> ! {
|
||||
execute!(
|
||||
stdout(),
|
||||
PopKeyboardEnhancementFlags,
|
||||
|
|
@ -62,6 +76,11 @@ impl Term {
|
|||
.ok();
|
||||
|
||||
disable_raw_mode().ok();
|
||||
|
||||
if let Some(words) = last_words {
|
||||
std::io::stdout().write_all(words).ok();
|
||||
}
|
||||
|
||||
std::process::exit(f() as i32);
|
||||
}
|
||||
|
||||
|
|
@ -102,15 +121,21 @@ impl Term {
|
|||
}
|
||||
|
||||
impl Drop for Term {
|
||||
fn drop(&mut self) { self.stop().ok(); }
|
||||
fn drop(&mut self) {
|
||||
self.stop().ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Term {
|
||||
type Target = Terminal<CrosstermBackend<Stdout>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Term {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue