diff --git a/Cargo.lock b/Cargo.lock index a2125464c..6cb4202a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1774,9 +1774,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plist" @@ -2265,9 +2265,9 @@ dependencies = [ [[package]] name = "simdutf8" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slab" @@ -2400,18 +2400,18 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", diff --git a/yazi-adapter/src/lib.rs b/yazi-adapter/src/lib.rs index 0698d13cb..206646918 100644 --- a/yazi-adapter/src/lib.rs +++ b/yazi-adapter/src/lib.rs @@ -40,7 +40,7 @@ static SHOWN: RoCell> = RoCell::n pub fn init() { // Tmux support - TMUX.init(env_exists("TMUX") && env_exists("TMUX_PANE")); + TMUX.init(env_exists("TMUX_PANE") && env_exists("TMUX")); ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" }); START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" }); CLOSE.init(if *TMUX { "\x1b\\" } else { "" }); diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index dbc39872c..0b87229f7 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -23,8 +23,6 @@ tokio = { workspace = true } toml_edit = "0.22.21" [build-dependencies] -yazi-shared = { path = "../yazi-shared", version = "0.3.3" } - # External build dependencies anyhow = { workspace = true } clap = { workspace = true } diff --git a/yazi-core/src/manager/commands/peek.rs b/yazi-core/src/manager/commands/peek.rs index a664b87c1..2c1c1f145 100644 --- a/yazi-core/src/manager/commands/peek.rs +++ b/yazi-core/src/manager/commands/peek.rs @@ -27,13 +27,17 @@ impl From for Opt { impl Manager { pub fn peek(&mut self, opt: impl Into) { let Some(hovered) = self.hovered().cloned() else { - return render!(self.active_mut().preview.reset()); + return self.active_mut().preview.reset(); }; + let mime = self.mimetype.get_owned(&hovered.url).unwrap_or_default(); let folder = self.active().hovered_folder().map(|f| (f.offset, f.cha)); + if !self.active().preview.same_url(&hovered.url) { self.active_mut().preview.skip = folder.map(|f| f.0).unwrap_or_default(); - render!(self.active_mut().preview.reset()); + } + if !self.active().preview.same_file(&hovered, &mime) { + self.active_mut().preview.reset(); } let opt = opt.into() as Opt; @@ -52,13 +56,8 @@ impl Manager { if hovered.is_dir() { self.active_mut().preview.go_folder(hovered, folder.map(|f| f.1), opt.force); - return; - } - - let mime = self.mimetype.get_owned(&hovered.url).unwrap_or_default(); - if !mime.is_empty() { - // Wait till mimetype is resolved to avoid flickering - self.active_mut().preview.go(hovered, &mime, opt.force); + } else { + self.active_mut().preview.go(hovered, mime.into(), opt.force); } } } diff --git a/yazi-core/src/manager/commands/seek.rs b/yazi-core/src/manager/commands/seek.rs index ba52aa642..8f2de7219 100644 --- a/yazi-core/src/manager/commands/seek.rs +++ b/yazi-core/src/manager/commands/seek.rs @@ -16,7 +16,7 @@ impl From for Opt { impl Manager { pub fn seek(&mut self, opt: impl Into) { let Some(hovered) = self.hovered() else { - return render!(self.active_mut().preview.reset()); + return self.active_mut().preview.reset(); }; let mime = if hovered.is_dir() { @@ -24,11 +24,11 @@ impl Manager { } else if let Some(s) = self.mimetype.get(&hovered.url) { s } else { - return render!(self.active_mut().preview.reset()); + return self.active_mut().preview.reset(); }; let Some(previewer) = PLUGIN.previewer(&hovered.url, mime) else { - return render!(self.active_mut().preview.reset()); + return self.active_mut().preview.reset(); }; let opt = opt.into() as Opt; diff --git a/yazi-core/src/tab/commands/preview.rs b/yazi-core/src/tab/commands/preview.rs index ed838b828..a608b8864 100644 --- a/yazi-core/src/tab/commands/preview.rs +++ b/yazi-core/src/tab/commands/preview.rs @@ -18,7 +18,7 @@ impl TryFrom for Opt { impl Tab { pub fn preview(&mut self, opt: impl TryInto) { let Some(hovered) = self.current.hovered().map(|h| &h.url) else { - return render!(self.preview.reset()); + return self.preview.reset(); }; let Ok(opt) = opt.try_into() else { diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index a3cf2a823..1c6cae829 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{borrow::Cow, ops::Not, time::Duration}; use tokio::{pin, task::JoinHandle}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; @@ -7,7 +7,7 @@ use yazi_adapter::ADAPTOR; use yazi_config::PLUGIN; use yazi_fs::Files; use yazi_plugin::{external::Highlighter, isolate, utils::PreviewLock}; -use yazi_shared::{MIME_DIR, fs::{Cha, File, FilesOp, Url}}; +use yazi_shared::{MIME_DIR, fs::{Cha, File, FilesOp, Url}, render}; #[derive(Default)] pub struct Preview { @@ -19,32 +19,30 @@ pub struct Preview { } impl Preview { - pub fn go(&mut self, file: File, mime: &str, force: bool) { - if !force && self.content_unchanged(&file.url, file.cha) { + pub fn go(&mut self, file: File, mime: Cow<'static, str>, force: bool) { + if mime.is_empty() { + return; // Wait till mimetype is resolved to avoid flickering + } else if !force && self.same_lock(&file, &mime) { return; } - let Some(previewer) = PLUGIN.previewer(&file.url, mime) else { - self.reset(); - return; + let Some(previewer) = PLUGIN.previewer(&file.url, &mime) else { + return self.reset(); }; self.abort(); if previewer.sync { - isolate::peek_sync(&previewer.run, file, self.skip); + isolate::peek_sync(&previewer.run, file, mime, self.skip); } else { - self.previewer_ct = Some(isolate::peek(&previewer.run, file, self.skip)); + self.previewer_ct = Some(isolate::peek(&previewer.run, file, mime, self.skip)); } } pub fn go_folder(&mut self, file: File, dir: Option, force: bool) { - let (cha, cwd) = (file.cha, file.url_owned()); - self.go(file, MIME_DIR, force); - - if self.content_unchanged(&cwd, cha) { - return; - } + let cwd = self.same_file(&file, MIME_DIR).not().then(|| file.url_owned()); + self.go(file, Cow::Borrowed(MIME_DIR), force); + let Some(cwd) = cwd else { return }; self.folder_loader.take().map(|h| h.abort()); self.folder_loader = Some(tokio::spawn(async move { let Some(new) = Files::assert_stale(&cwd, dir.unwrap_or(Cha::dummy())).await else { @@ -71,10 +69,10 @@ impl Preview { } #[inline] - pub fn reset(&mut self) -> bool { + pub fn reset(&mut self) { self.abort(); ADAPTOR.image_hide().ok(); - self.lock.take().is_some() + render!(self.lock.take().is_some()) } #[inline] @@ -84,15 +82,16 @@ impl Preview { } #[inline] - pub fn same_url(&self, url: &Url) -> bool { - matches!(self.lock, Some(ref lock) if lock.url == *url) + pub fn same_url(&self, url: &Url) -> bool { self.lock.as_ref().is_some_and(|l| *url == l.url) } + + #[inline] + pub fn same_file(&self, file: &File, mime: &str) -> bool { + self.same_url(&file.url) + && self.lock.as_ref().is_some_and(|l| file.cha.hits(l.cha) && mime == l.mime) } #[inline] - fn content_unchanged(&self, url: &Url, cha: Cha) -> bool { - match &self.lock { - Some(l) => *url == l.url && self.skip == l.skip && cha.hits(l.cha), - None => false, - } + pub fn same_lock(&self, file: &File, mime: &str) -> bool { + self.same_file(file, mime) && self.lock.as_ref().is_some_and(|l| self.skip == l.skip) } } diff --git a/yazi-fm/src/app/commands/render.rs b/yazi-fm/src/app/commands/render.rs index e61421e4c..6fd735739 100644 --- a/yazi-fm/src/app/commands/render.rs +++ b/yazi-fm/src/app/commands/render.rs @@ -4,14 +4,14 @@ use crossterm::{execute, queue, terminal::{BeginSynchronizedUpdate, EndSynchroni use ratatui::{CompletedFrame, backend::{Backend, CrosstermBackend}, buffer::Buffer}; use scopeguard::defer; use yazi_plugin::elements::COLLISION; +use yazi_shared::event::NEED_RENDER; use crate::{app::App, lives::Lives, root::Root}; impl App { pub(crate) fn render(&mut self) { - let Some(term) = &mut self.term else { - return; - }; + NEED_RENDER.store(false, Ordering::Relaxed); + let Some(term) = &mut self.term else { return }; queue!(stderr(), BeginSynchronizedUpdate).ok(); defer! { execute!(stderr(), EndSynchronizedUpdate).ok(); } diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index bbb7be0ba..a4e7e5e50 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use mlua::{ExternalError, ExternalResult, HookTriggers, Table, TableExt}; use tokio::{runtime::Handle, select}; use tokio_util::sync::CancellationToken; @@ -8,7 +10,12 @@ use yazi_shared::{Layer, emit, event::Cmd}; use super::slim_lua; use crate::{LUA, Opt, OptCallback, bindings::{Cast, Window}, elements::Rect, file::File, loader::LOADER}; -pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> CancellationToken { +pub fn peek( + cmd: &Cmd, + file: yazi_shared::fs::File, + mime: Cow<'static, str>, + skip: usize, +) -> CancellationToken { let ct = CancellationToken::new(); let name = cmd.name.to_owned(); @@ -31,6 +38,7 @@ pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> Cancellation return Err("unloaded plugin".into_lua_err()); }; plugin.raw_set("file", File::cast(&lua, file)?)?; + plugin.raw_set("_mime", mime)?; plugin.raw_set("skip", skip)?; plugin.raw_set("area", Rect::cast(&lua, LAYOUT.load().preview)?)?; plugin.raw_set("window", Window::default())?; @@ -55,9 +63,10 @@ pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> Cancellation ct } -pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) { +pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, mime: Cow<'static, str>, skip: usize) { let cb: OptCallback = Box::new(move |_, plugin| { plugin.raw_set("file", File::cast(&LUA, file)?)?; + plugin.raw_set("_mime", mime)?; plugin.raw_set("skip", skip)?; plugin.raw_set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?; plugin.raw_set("window", Window::default())?; diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 731576616..b409c5618 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -6,8 +6,9 @@ use super::Utils; use crate::{bindings::Window, cast_to_renderable, elements::{Paragraph, RectRef, Renderable, WRAP, WRAP_NO}, external::Highlighter, file::FileRef}; pub struct PreviewLock { - pub url: yazi_shared::fs::Url, - pub cha: yazi_shared::fs::Cha, + pub url: yazi_shared::fs::Url, + pub cha: yazi_shared::fs::Cha, + pub mime: String, pub skip: usize, pub window: Window, @@ -18,10 +19,12 @@ impl<'a> TryFrom> for PreviewLock { type Error = mlua::Error; fn try_from(t: Table) -> Result { - let file: FileRef = t.raw_get("file")?; + let file: FileRef = t.raw_get("file")?; // TODO: use `_file` instead of `file` Ok(Self { - cha: file.cha, - url: file.url_owned(), + url: file.url_owned(), + cha: file.cha, + mime: t.raw_get("_mime")?, + skip: t.raw_get("skip")?, window: t.raw_get("window")?, data: Default::default(),