fix: one file's mime-type changed multiple times without triggering a preview again (#1682)

This commit is contained in:
三咲雅 · Misaki Masa 2024-09-26 00:22:55 +08:00 committed by GitHub
parent 631afd09ee
commit 5b66192332
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 66 additions and 58 deletions

16
Cargo.lock generated
View file

@ -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",

View file

@ -40,7 +40,7 @@ static SHOWN: RoCell<arc_swap::ArcSwapOption<ratatui::layout::Rect>> = 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 { "" });

View file

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

View file

@ -27,13 +27,17 @@ impl From<bool> for Opt {
impl Manager {
pub fn peek(&mut self, opt: impl Into<Opt>) {
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);
}
}
}

View file

@ -16,7 +16,7 @@ impl From<Cmd> for Opt {
impl Manager {
pub fn seek(&mut self, opt: impl Into<Opt>) {
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;

View file

@ -18,7 +18,7 @@ impl TryFrom<Cmd> for Opt {
impl Tab {
pub fn preview(&mut self, opt: impl TryInto<Opt>) {
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 {

View file

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

View file

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

View file

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

View file

@ -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<Table<'a>> for PreviewLock {
type Error = mlua::Error;
fn try_from(t: Table) -> Result<Self, Self::Error> {
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(),