added option to pass opened file to stdout

Signed-off-by: aserowy <serowy@hotmail.com>
This commit is contained in:
aserowy 2024-01-06 15:01:30 +01:00
parent 93c8d90a51
commit d78eafb37c
10 changed files with 159 additions and 58 deletions

View file

@ -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)]

View file

@ -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
}
}

View file

@ -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));
}
}
});

View file

@ -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>) {

View file

@ -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 {

View file

@ -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();
}
}
}

View file

@ -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,
);
}));
}
}

View file

@ -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;
}
}

View file

@ -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();
};

View file

@ -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
}
}