diff --git a/yazi-core/src/help/commands/escape.rs b/yazi-core/src/help/commands/escape.rs index afde74e2..d0569676 100644 --- a/yazi-core/src/help/commands/escape.rs +++ b/yazi-core/src/help/commands/escape.rs @@ -2,14 +2,8 @@ use yazi_shared::event::Exec; use crate::help::Help; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Help { - pub fn escape(&mut self, _: impl Into) -> bool { + pub fn escape(&mut self, _: &Exec) -> bool { if self.in_filter.is_some() { self.in_filter = None; self.filter_apply(); diff --git a/yazi-core/src/help/commands/filter.rs b/yazi-core/src/help/commands/filter.rs index b6580b2d..5a93c07b 100644 --- a/yazi-core/src/help/commands/filter.rs +++ b/yazi-core/src/help/commands/filter.rs @@ -3,14 +3,8 @@ use yazi_shared::event::Exec; use crate::{help::Help, input::Input}; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Help { - pub fn filter(&mut self, _: impl Into) -> bool { + pub fn filter(&mut self, _: &Exec) -> bool { let mut input = Input::default(); input.position = Position::new(Origin::BottomLeft, Offset::line()); diff --git a/yazi-core/src/input/commands/backward.rs b/yazi-core/src/input/commands/backward.rs index 3fe3b0b7..4131ad02 100644 --- a/yazi-core/src/input/commands/backward.rs +++ b/yazi-core/src/input/commands/backward.rs @@ -2,14 +2,8 @@ use yazi_shared::{event::Exec, CharKind}; use crate::input::Input; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Input { - pub fn backward(&mut self, _: impl Into) -> bool { + pub fn backward(&mut self, _: &Exec) -> bool { let snap = self.snap(); if snap.cursor == 0 { return self.move_(0); diff --git a/yazi-core/src/input/commands/redo.rs b/yazi-core/src/input/commands/redo.rs index 9573f0e1..135238e3 100644 --- a/yazi-core/src/input/commands/redo.rs +++ b/yazi-core/src/input/commands/redo.rs @@ -2,12 +2,6 @@ use yazi_shared::event::Exec; use crate::input::Input; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Input { - pub fn redo(&mut self, _: impl Into) -> bool { self.snaps.redo() } + pub fn redo(&mut self, _: &Exec) -> bool { self.snaps.redo() } } diff --git a/yazi-core/src/input/commands/undo.rs b/yazi-core/src/input/commands/undo.rs index 12f63ceb..8ba4b729 100644 --- a/yazi-core/src/input/commands/undo.rs +++ b/yazi-core/src/input/commands/undo.rs @@ -2,14 +2,8 @@ use yazi_shared::event::Exec; use crate::input::{Input, InputMode}; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Input { - pub fn undo(&mut self, _: impl Into) -> bool { + pub fn undo(&mut self, _: &Exec) -> bool { if !self.snaps.undo() { return false; } diff --git a/yazi-core/src/input/commands/visual.rs b/yazi-core/src/input/commands/visual.rs index 0ce945ac..67ba3fcb 100644 --- a/yazi-core/src/input/commands/visual.rs +++ b/yazi-core/src/input/commands/visual.rs @@ -2,15 +2,9 @@ use yazi_shared::event::Exec; use crate::input::{op::InputOp, Input, InputMode}; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Input { #[inline] - pub fn visual(&mut self, _: impl Into) -> bool { + pub fn visual(&mut self, _: &Exec) -> bool { let snap = self.snap_mut(); if snap.mode != InputMode::Normal { return false; diff --git a/yazi-core/src/input/commands/yank.rs b/yazi-core/src/input/commands/yank.rs index c56f653b..14ee9914 100644 --- a/yazi-core/src/input/commands/yank.rs +++ b/yazi-core/src/input/commands/yank.rs @@ -2,14 +2,8 @@ use yazi_shared::event::Exec; use crate::input::{op::InputOp, Input}; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Input { - pub fn yank(&mut self, _: impl Into) -> bool { + pub fn yank(&mut self, _: &Exec) -> bool { match self.snap().op { InputOp::None => { self.snap_mut().op = InputOp::Yank(self.snap().cursor); diff --git a/yazi-core/src/manager/commands/close.rs b/yazi-core/src/manager/commands/close.rs index 7249a111..47e9478e 100644 --- a/yazi-core/src/manager/commands/close.rs +++ b/yazi-core/src/manager/commands/close.rs @@ -2,14 +2,8 @@ use yazi_shared::event::Exec; use crate::{manager::Manager, tasks::Tasks}; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Manager { - pub fn close(&mut self, _: impl Into, tasks: &Tasks) -> bool { + pub fn close(&mut self, _: &Exec, tasks: &Tasks) -> bool { if self.tabs.len() > 1 { return self.tabs.close(self.tabs.idx); } diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index d8365e75..b8603881 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -1,72 +1,117 @@ use std::ffi::OsString; -use yazi_config::{popup::SelectCfg, OPEN}; -use yazi_shared::{event::Exec, MIME_DIR}; +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 crate::{manager::Manager, select::Select, tasks::Tasks}; pub struct Opt { + targets: Option)>>, interactive: bool, } impl From<&Exec> for Opt { - fn from(e: &Exec) -> Self { Self { interactive: e.named.contains_key("interactive") } } + fn from(e: &Exec) -> Self { + Self { targets: e.take_data(), interactive: e.named.contains_key("interactive") } + } } impl Manager { - async fn open_interactive(files: Vec<(OsString, String)>) { - let openers = OPEN.common_openers(&files); - if openers.is_empty() { - return; - } - - let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect())); - if let Ok(choice) = result.await { - Tasks::_open(files, Some(openers[choice].clone())); - } - } - - pub fn open(&mut self, opt: impl Into) -> bool { - let mut files: Vec<_> = self - .selected() - .into_iter() - .map(|f| { - ( - f.url(), - f.is_dir().then(|| MIME_DIR.to_owned()).or_else(|| self.mimetype.get(&f.url).cloned()), - ) - }) - .collect(); - - if files.is_empty() { + pub fn open(&mut self, opt: impl Into, tasks: &Tasks) -> bool { + let selected = self.selected(); + if selected.is_empty() { + return false; + } else if Self::quit_with_selected(&selected) { return false; } - let opt = opt.into() as Opt; - // TODO: plugin system - // tokio::spawn(async move { - // let todo: Vec<_> = files.iter().filter(|(_, m)| m.is_none()).map(|(u, _)| - // u).collect(); if let Ok(mut mimes) = external::file(&todo).await { - // files = files - // .into_iter() - // .map(|(u, m)| { - // let mime = m.or_else(|| mimes.remove(&u)); - // (u, mime) - // }) - // .collect(); - // } + let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]); + for f in selected { + if f.is_dir() { + done.push((f.url(), Some(MIME_DIR.to_owned()))); + } else if self.mimetype.get(&f.url).is_some() { + done.push((f.url(), None)); + } else { + todo.push(f.clone()); + } + } - // let files: Vec<_> = - // files.into_iter().filter_map(|(u, m)| m.map(|m| (u.into_os_string(), - // m))).collect(); + let mut opt = opt.into() as Opt; + if todo.is_empty() { + opt.targets = Some(done); + return self.open_do(opt, tasks); + } - // if opt.interactive { - // Self::open_interactive(files).await; - // return; - // } + tokio::spawn(async move { + done.extend(todo.iter().map(|f| (f.url(), None))); + if let Err(e) = isolate::preload("mime.lua".to_string(), todo, true).await { + error!("preload in watcher failed: {e}"); + } - // Tasks::_open(files, None); - // }); + Self::_open_do(opt.interactive, done); + }); false } + + #[inline] + pub fn _open_do(interactive: bool, targets: Vec<(Url, Option)>) { + emit!(Call( + Exec::call("open_do", vec![]).with_bool("interactive", interactive).with_data(targets).vec(), + Layer::Manager + )); + } + + pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) -> bool { + let opt = opt.into() as Opt; + let Some(targets) = opt.targets else { + return false; + }; + + let targets: Vec<_> = targets + .into_iter() + .filter_map(|(u, m)| m.or_else(|| self.mimetype.get(&u).cloned()).map(|m| (u, m))) + .collect(); + + if targets.is_empty() { + return false; + } else if !opt.interactive { + tasks.file_open(&targets); + return false; + } + + let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect(); + if openers.is_empty() { + return false; + } + + let urls = 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())); + if let Ok(choice) = result.await { + Tasks::_open(urls, openers[choice].clone()); + } + }); + false + } + + fn quit_with_selected(selected: &[&File]) -> bool { + let Some(p) = ARGS.chooser_file.clone() else { + return false; + }; + + let paths = selected.iter().fold(OsString::new(), |mut s, &f| { + s.push(f.url.as_os_str()); + s.push("\n"); + s + }); + + tokio::spawn(async move { + fs::write(p, paths.as_encoded_bytes()).await.ok(); + emit!(Quit(false)); + }); + true + } } diff --git a/yazi-core/src/manager/commands/refresh.rs b/yazi-core/src/manager/commands/refresh.rs index e362e683..8b56efd8 100644 --- a/yazi-core/src/manager/commands/refresh.rs +++ b/yazi-core/src/manager/commands/refresh.rs @@ -4,19 +4,13 @@ use yazi_shared::{emit, event::Exec, Layer}; use crate::manager::Manager; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Manager { #[inline] pub fn _refresh() { emit!(Call(Exec::call("refresh", vec![]).vec(), Layer::Manager)); } - pub fn refresh(&mut self, _: impl Into) -> bool { + pub fn refresh(&mut self, _: &Exec) -> bool { env::set_current_dir(self.cwd()).ok(); env::set_var("PWD", self.cwd()); diff --git a/yazi-core/src/manager/commands/suspend.rs b/yazi-core/src/manager/commands/suspend.rs index 4c6def2b..b1865de1 100644 --- a/yazi-core/src/manager/commands/suspend.rs +++ b/yazi-core/src/manager/commands/suspend.rs @@ -3,13 +3,8 @@ use yazi_shared::event::Exec; use crate::manager::Manager; -pub struct Opt; -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Manager { - pub fn suspend(&mut self, _: impl Into) -> bool { + pub fn suspend(&mut self, _: &Exec) -> bool { #[cfg(unix)] tokio::spawn(async move { Scheduler::app_stop().await; diff --git a/yazi-core/src/manager/tabs.rs b/yazi-core/src/manager/tabs.rs index b029aa11..cb0e3d6d 100644 --- a/yazi-core/src/manager/tabs.rs +++ b/yazi-core/src/manager/tabs.rs @@ -1,4 +1,3 @@ -use yazi_adaptor::ADAPTOR; use yazi_config::BOOT; use yazi_shared::fs::Url; @@ -11,12 +10,12 @@ pub struct Tabs { impl Tabs { pub fn make() -> Self { - let mut tabs = Self { idx: usize::MAX, items: vec![Tab::from(Url::from(&BOOT.cwd))] }; + let mut tabs = Self { idx: 0, items: vec![Tab::from(Url::from(&BOOT.cwd))] }; if let Some(file) = &BOOT.file { tabs.items[0].reveal(Url::from(BOOT.cwd.join(file))); } - tabs.set_idx(0); + Manager::_refresh(); tabs } @@ -31,10 +30,16 @@ impl Tabs { #[inline] pub(super) fn set_idx(&mut self, idx: usize) { + if self.idx == idx { + return; + } + + // We must reset it before changing the tab index. + self.active_mut().preview.reset_image(); + self.idx = idx; - // TODO: plugin system - // self.active_mut().preview.reset_image(); Manager::_refresh(); + Manager::_peek(true); } } diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index f98c4a89..e6454a11 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -21,13 +21,9 @@ impl<'a> From<&'a Exec> for Opt { impl Tab { pub fn shell(&self, opt: impl Into) -> bool { - let selected: Vec<_> = self - .selected() - .into_iter() - .map(|f| (f.url.as_os_str().to_owned(), Default::default())) - .collect(); - let mut opt = opt.into() as Opt; + let selected: Vec<_> = self.selected().into_iter().map(|f| f.url()).collect(); + tokio::spawn(async move { if !opt.confirm || opt.cmd.is_empty() { let mut result = Input::_show(InputCfg::shell(opt.block).with_value(opt.cmd)); @@ -37,19 +33,15 @@ impl Tab { } } - Tasks::_open( - selected, - Some(Opener { - exec: opt.cmd, - block: opt.block, - orphan: false, - desc: Default::default(), - for_: None, - spread: true, - }), - ); + Tasks::_open(selected, Opener { + exec: opt.cmd, + block: opt.block, + orphan: false, + desc: Default::default(), + for_: None, + spread: true, + }); }); - false } } diff --git a/yazi-core/src/tasks/commands/cancel.rs b/yazi-core/src/tasks/commands/cancel.rs index 013d10ed..6800b175 100644 --- a/yazi-core/src/tasks/commands/cancel.rs +++ b/yazi-core/src/tasks/commands/cancel.rs @@ -2,14 +2,8 @@ use yazi_shared::event::Exec; use crate::tasks::Tasks; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Tasks { - pub fn cancel(&mut self, _: impl Into) -> bool { + pub fn cancel(&mut self, _: &Exec) -> bool { let id = self.scheduler.running.read().get_id(self.cursor); if id.map(|id| self.scheduler.cancel(id)) != Some(true) { return false; diff --git a/yazi-core/src/tasks/commands/inspect.rs b/yazi-core/src/tasks/commands/inspect.rs index df72508e..8c5d1782 100644 --- a/yazi-core/src/tasks/commands/inspect.rs +++ b/yazi-core/src/tasks/commands/inspect.rs @@ -7,14 +7,8 @@ use yazi_shared::{event::Exec, term::Term, Defer}; use crate::tasks::Tasks; -pub struct Opt; - -impl From<&Exec> for Opt { - fn from(_: &Exec) -> Self { Self } -} - impl Tasks { - pub fn inspect(&self, _: impl Into) -> bool { + pub fn inspect(&self, _: &Exec) -> bool { let Some(id) = self.scheduler.running.read().get_id(self.cursor) else { return false; }; diff --git a/yazi-core/src/tasks/commands/open.rs b/yazi-core/src/tasks/commands/open.rs index bd0ac9a8..5b831e31 100644 --- a/yazi-core/src/tasks/commands/open.rs +++ b/yazi-core/src/tasks/commands/open.rs @@ -1,14 +1,12 @@ -use std::ffi::OsString; - use anyhow::anyhow; -use yazi_config::{open::Opener, ARGS}; -use yazi_shared::{emit, event::Exec, Layer}; +use yazi_config::open::Opener; +use yazi_shared::{emit, event::Exec, fs::Url, Layer}; use crate::tasks::Tasks; pub struct Opt { - targets: Vec<(OsString, String)>, - opener: Option, + targets: Vec, + opener: Opener, } impl TryFrom<&Exec> for Opt { @@ -20,31 +18,13 @@ impl TryFrom<&Exec> for Opt { } impl Tasks { - pub fn _open(targets: Vec<(OsString, String)>, opener: Option) { + pub fn _open(targets: Vec, opener: Opener) { emit!(Call(Exec::call("open", vec![]).with_data(Opt { targets, opener }).vec(), Layer::Tasks)); } pub fn open(&mut self, opt: impl TryInto) -> bool { - let Ok(opt) = opt.try_into() else { - return false; - }; - - if let Some(p) = &ARGS.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::>()); - } else { - self.file_open(&opt.targets); + if let Ok(opt) = opt.try_into() { + self.file_open_with(&opt.opener, &opt.targets); } false } diff --git a/yazi-fm/src/app/commands/stop.rs b/yazi-fm/src/app/commands/stop.rs index f0ded8eb..e5c92bd8 100644 --- a/yazi-fm/src/app/commands/stop.rs +++ b/yazi-fm/src/app/commands/stop.rs @@ -1,7 +1,5 @@ use anyhow::Result; use tokio::sync::oneshot; -use yazi_adaptor::ADAPTOR; -use yazi_core::manager::Manager; use yazi_shared::{emit, event::Exec, term::Term}; use crate::app::App; diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 0658e78c..58a8202b 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -137,7 +137,8 @@ impl<'a> Executor<'a> { on!(ACTIVE, visual_mode); // Operation - on!(MANAGER, open); + on!(MANAGER, open, &self.app.cx.tasks); + on!(MANAGER, open_do, &self.app.cx.tasks); on!(MANAGER, yank); on!(MANAGER, paste, &self.app.cx.tasks); on!(MANAGER, link, &self.app.cx.tasks); diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index c90d5800..62478fa4 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -177,17 +177,22 @@ impl<'a, 'b> Folder<'a, 'b> { .iter() .skip(window.0) .take(window.1) - .filter_map(|f| self.file(f).ok()) + .enumerate() + .filter_map(|(i, f)| self.file(i, f).ok()) .collect::>(), )?; ud.set_named_user_value("files", self.scope.create_any_userdata_ref(&self.inner.files)?)?; - ud.set_named_user_value("hovered", self.inner.hovered().and_then(|h| self.file(h).ok()))?; + ud.set_named_user_value( + "hovered", + self.inner.hovered().and_then(|h| self.file(self.inner.cursor - window.0, h).ok()), + )?; Ok(ud) } - fn file(&self, inner: &'a yazi_shared::fs::File) -> mlua::Result> { + fn file(&self, idx: usize, inner: &'a yazi_shared::fs::File) -> mlua::Result> { let ud = self.scope.create_any_userdata_ref(inner)?; + ud.set_named_user_value("idx", idx)?; ud.set_named_user_value("folder", self.scope.create_any_userdata_ref(self.inner)?)?; Ok(ud)