fix: use a unique Id for each tab (#1826)

This commit is contained in:
三咲雅 · Misaki Masa 2024-10-24 08:57:08 +08:00 committed by GitHub
parent 35d781e240
commit e8c1d625ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 143 additions and 82 deletions

View file

@ -2,21 +2,18 @@ use std::{collections::HashSet, path::PathBuf};
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_macro::render; use yazi_macro::render;
use yazi_shared::{event::{Cmd, Data}, fs::{Url, Urn}}; use yazi_shared::{Id, event::{Cmd, Data}, fs::{Url, Urn}};
use crate::manager::Manager; use crate::manager::Manager;
struct Opt { struct Opt {
url: Option<Url>, url: Option<Url>,
tab: Option<usize>, tab: Option<Id>,
} }
impl From<Cmd> for Opt { impl From<Cmd> for Opt {
fn from(mut c: Cmd) -> Self { fn from(mut c: Cmd) -> Self {
Self { Self { url: c.take_first().and_then(Data::into_url), tab: c.get("tab").and_then(Data::as_id) }
url: c.take_first().and_then(Data::into_url),
tab: c.get("tab").and_then(Data::as_usize),
}
} }
} }
impl From<Option<Url>> for Opt { impl From<Option<Url>> for Opt {
@ -49,10 +46,10 @@ impl Manager {
self.watcher.watch(to_watch); self.watcher.watch(to_watch);
// Publish through DDS // Publish through DDS
Pubsub::pub_from_hover(self.active().idx, self.hovered().map(|h| &h.url)); Pubsub::pub_from_hover(self.active().id, self.hovered().map(|h| &h.url));
} }
fn hover_do(&mut self, url: Url, tab: Option<usize>) { fn hover_do(&mut self, url: Url, tab: Option<Id>) {
// Hover on the file // Hover on the file
if let Ok(p) = url.strip_prefix(&self.current_or(tab).url).map(PathBuf::from) { if let Ok(p) = url.strip_prefix(&self.current_or(tab).url).map(PathBuf::from) {
render!(self.current_or_mut(tab).repos(Some(Urn::new(&p)))); render!(self.current_or_mut(tab).repos(Some(Urn::new(&p))));

View file

@ -30,7 +30,6 @@ impl Tabs {
self.set_idx(self.absolute(1)); self.set_idx(self.absolute(1));
} }
self.reorder();
render!(); render!();
} }
} }

View file

@ -36,8 +36,7 @@ impl Tabs {
return; return;
} }
let mut tab = Tab { idx: self.cursor + 1, ..Default::default() }; let mut tab = Tab::default();
if !opt.current { if !opt.current {
tab.cd(opt.url); tab.cd(opt.url);
} else if let Some(h) = self.active().hovered() { } else if let Some(h) = self.active().hovered() {
@ -52,7 +51,6 @@ impl Tabs {
self.items.insert(self.cursor + 1, tab); self.items.insert(self.cursor + 1, tab);
self.set_idx(self.cursor + 1); self.set_idx(self.cursor + 1);
self.reorder();
render!(); render!();
} }
} }

View file

@ -21,7 +21,6 @@ impl Tabs {
self.items.swap(self.cursor, idx); self.items.swap(self.cursor, idx);
self.set_idx(idx); self.set_idx(idx);
self.reorder();
render!(); render!();
} }
} }

View file

@ -87,7 +87,7 @@ impl Manager {
return; return;
} }
ManagerProxy::hover(None, tab.idx); // Re-hover in next loop ManagerProxy::hover(None, tab.id); // Re-hover in next loop
ManagerProxy::update_paged(); // Update for paged files in next loop ManagerProxy::update_paged(); // Update for paged files in next loop
if calc { if calc {
tasks.prework_sorted(&tab.current.files); tasks.prework_sorted(&tab.current.files);

View file

@ -2,7 +2,7 @@ use ratatui::layout::Rect;
use yazi_adapter::Dimension; use yazi_adapter::Dimension;
use yazi_config::popup::{Origin, Position}; use yazi_config::popup::{Origin, Position};
use yazi_fs::Folder; use yazi_fs::Folder;
use yazi_shared::fs::{File, Url}; use yazi_shared::{Id, fs::{File, Url}};
use super::{Mimetype, Tabs, Watcher, Yanked}; use super::{Mimetype, Tabs, Watcher, Yanked};
use crate::tab::Tab; use crate::tab::Tab;
@ -48,10 +48,10 @@ impl Manager {
pub fn active_mut(&mut self) -> &mut Tab { self.tabs.active_mut() } pub fn active_mut(&mut self) -> &mut Tab { self.tabs.active_mut() }
#[inline] #[inline]
pub fn active_or(&self, idx: Option<usize>) -> &Tab { self.tabs.active_or(idx) } pub fn active_or(&self, id: Option<Id>) -> &Tab { self.tabs.active_or(id) }
#[inline] #[inline]
pub fn active_or_mut(&mut self, idx: Option<usize>) -> &mut Tab { self.tabs.active_or_mut(idx) } pub fn active_or_mut(&mut self, id: Option<Id>) -> &mut Tab { self.tabs.active_or_mut(id) }
#[inline] #[inline]
pub fn current(&self) -> &Folder { &self.active().current } pub fn current(&self) -> &Folder { &self.active().current }
@ -60,10 +60,10 @@ impl Manager {
pub fn current_mut(&mut self) -> &mut Folder { &mut self.active_mut().current } pub fn current_mut(&mut self) -> &mut Folder { &mut self.active_mut().current }
#[inline] #[inline]
pub fn current_or(&self, idx: Option<usize>) -> &Folder { &self.active_or(idx).current } pub fn current_or(&self, idx: Option<Id>) -> &Folder { &self.active_or(idx).current }
#[inline] #[inline]
pub fn current_or_mut(&mut self, idx: Option<usize>) -> &mut Folder { pub fn current_or_mut(&mut self, idx: Option<Id>) -> &mut Folder {
&mut self.active_or_mut(idx).current &mut self.active_or_mut(idx).current
} }

View file

@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut};
use yazi_boot::BOOT; use yazi_boot::BOOT;
use yazi_dds::Pubsub; use yazi_dds::Pubsub;
use yazi_proxy::ManagerProxy; use yazi_proxy::ManagerProxy;
use yazi_shared::fs::Url; use yazi_shared::{Id, fs::Url};
use crate::tab::Tab; use crate::tab::Tab;
@ -16,7 +16,6 @@ impl Tabs {
pub fn make() -> Self { pub fn make() -> Self {
let mut tabs = let mut tabs =
Self { cursor: 0, items: (0..BOOT.cwds.len()).map(|_| Tab::default()).collect() }; Self { cursor: 0, items: (0..BOOT.cwds.len()).map(|_| Tab::default()).collect() };
tabs.reorder();
for (i, tab) in tabs.iter_mut().enumerate() { for (i, tab) in tabs.iter_mut().enumerate() {
let file = &BOOT.files[i]; let file = &BOOT.files[i];
@ -37,11 +36,6 @@ impl Tabs {
} }
} }
#[inline]
pub(super) fn reorder(&mut self) {
self.items.iter_mut().enumerate().for_each(|(i, tab)| tab.idx = i);
}
pub(super) fn set_idx(&mut self, idx: usize) { pub(super) fn set_idx(&mut self, idx: usize) {
// Reset the preview of the last active tab // Reset the preview of the last active tab
if let Some(active) = self.items.get_mut(self.cursor) { if let Some(active) = self.items.get_mut(self.cursor) {
@ -63,16 +57,16 @@ impl Tabs {
pub(super) fn active_mut(&mut self) -> &mut Tab { &mut self.items[self.cursor] } pub(super) fn active_mut(&mut self) -> &mut Tab { &mut self.items[self.cursor] }
#[inline] #[inline]
pub fn active_or(&self, idx: Option<usize>) -> &Tab { pub fn active_or(&self, id: Option<Id>) -> &Tab {
idx.and_then(|i| self.items.get(i)).unwrap_or(&self.items[self.cursor]) id.and_then(|id| self.iter().find(|&t| t.id == id)).unwrap_or(self.active())
} }
#[inline] #[inline]
pub(super) fn active_or_mut(&mut self, idx: Option<usize>) -> &mut Tab { pub(super) fn active_or_mut(&mut self, id: Option<Id>) -> &mut Tab {
if let Some(i) = idx.filter(|&i| i < self.items.len()) { if let Some(i) = id.and_then(|id| self.iter().position(|t| t.id == id)) {
&mut self.items[i] &mut self.items[i]
} else { } else {
&mut self.items[self.cursor] self.active_mut()
} }
} }
} }

View file

@ -42,7 +42,7 @@ impl Tab {
} }
} }
ManagerProxy::hover(None, self.idx); ManagerProxy::hover(None, self.id);
render!(); render!();
} }
} }

View file

@ -66,7 +66,7 @@ impl Tab {
self.backstack.push(opt.target.clone()); self.backstack.push(opt.target.clone());
} }
Pubsub::pub_from_cd(self.idx, self.cwd()); Pubsub::pub_from_cd(self.id, self.cwd());
ManagerProxy::refresh(); ManagerProxy::refresh();
render!(); render!();
} }

View file

@ -27,7 +27,7 @@ impl Tab {
self.current.repos(hovered.as_ref().map(|u| u.as_urn())); self.current.repos(hovered.as_ref().map(|u| u.as_urn()));
if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) { if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) {
ManagerProxy::hover(None, self.idx); ManagerProxy::hover(None, self.id);
} }
render!(); render!();

View file

@ -15,7 +15,7 @@ impl Tab {
self.apply_files_attrs(); self.apply_files_attrs();
if hovered.as_ref() != self.hovered().map(|f| &f.url) { if hovered.as_ref() != self.hovered().map(|f| &f.url) {
ManagerProxy::hover(hovered, self.idx); ManagerProxy::hover(hovered, self.id);
} else if self.hovered().is_some_and(|f| f.is_dir()) { } else if self.hovered().is_some_and(|f| f.is_dir()) {
ManagerProxy::peek(true); ManagerProxy::peek(true);
} }

View file

@ -30,6 +30,6 @@ impl Tab {
self.cd(parent.clone()); self.cd(parent.clone());
FilesOp::Creating(parent, vec![File::from_dummy(opt.target.clone(), None)]).emit(); FilesOp::Creating(parent, vec![File::from_dummy(opt.target.clone(), None)]).emit();
ManagerProxy::hover(Some(opt.target), self.idx); ManagerProxy::hover(Some(opt.target), self.id);
} }
} }

View file

@ -7,14 +7,13 @@ use yazi_adapter::Dimension;
use yazi_config::{LAYOUT, popup::{Origin, Position}}; use yazi_config::{LAYOUT, popup::{Origin, Position}};
use yazi_fs::{Folder, FolderStage}; use yazi_fs::{Folder, FolderStage};
use yazi_macro::render; use yazi_macro::render;
use yazi_shared::fs::{File, Url}; use yazi_shared::{Id, Ids, fs::{File, Url}};
use super::{Backstack, Config, Finder, History, Mode, Preview}; use super::{Backstack, Config, Finder, History, Mode, Preview};
use crate::tab::Selected; use crate::tab::Selected;
#[derive(Default)]
pub struct Tab { pub struct Tab {
pub idx: usize, pub id: Id,
pub mode: Mode, pub mode: Mode,
pub conf: Config, pub conf: Config,
pub current: Folder, pub current: Folder,
@ -29,6 +28,28 @@ pub struct Tab {
pub search: Option<JoinHandle<Result<()>>>, pub search: Option<JoinHandle<Result<()>>>,
} }
impl Default for Tab {
fn default() -> Self {
static IDS: Ids = Ids::new();
Self {
id: IDS.next(),
mode: Default::default(),
conf: Default::default(),
current: Default::default(),
parent: Default::default(),
backstack: Default::default(),
history: Default::default(),
selected: Default::default(),
preview: Default::default(),
finder: Default::default(),
search: Default::default(),
}
}
}
impl Tab { impl Tab {
pub fn shutdown(&mut self) { pub fn shutdown(&mut self) {
if let Some(handle) = self.search.take() { if let Some(handle) = self.search.take() {

View file

@ -2,13 +2,13 @@ use std::borrow::Cow;
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yazi_shared::fs::Url; use yazi_shared::{Id, fs::Url};
use super::Body; use super::Body;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct BodyCd<'a> { pub struct BodyCd<'a> {
pub tab: usize, pub tab: Id,
pub url: Cow<'a, Url>, pub url: Cow<'a, Url>,
#[serde(skip)] #[serde(skip)]
dummy: bool, dummy: bool,
@ -16,14 +16,14 @@ pub struct BodyCd<'a> {
impl<'a> BodyCd<'a> { impl<'a> BodyCd<'a> {
#[inline] #[inline]
pub fn borrowed(tab: usize, url: &'a Url) -> Body<'a> { pub fn borrowed(tab: Id, url: &'a Url) -> Body<'a> {
Self { tab, url: Cow::Borrowed(url), dummy: false }.into() Self { tab, url: Cow::Borrowed(url), dummy: false }.into()
} }
} }
impl BodyCd<'static> { impl BodyCd<'static> {
#[inline] #[inline]
pub fn dummy(tab: usize) -> Body<'static> { pub fn dummy(tab: Id) -> Body<'static> {
Self { tab, url: Default::default(), dummy: true }.into() Self { tab, url: Default::default(), dummy: true }.into()
} }
} }
@ -36,11 +36,11 @@ impl IntoLua<'_> for BodyCd<'static> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
if let Some(Cow::Owned(url)) = Some(self.url).filter(|_| !self.dummy) { if let Some(Cow::Owned(url)) = Some(self.url).filter(|_| !self.dummy) {
lua.create_table_from([ lua.create_table_from([
("tab", self.tab.into_lua(lua)?), ("tab", self.tab.get().into_lua(lua)?),
("url", lua.create_any_userdata(url)?.into_lua(lua)?), ("url", lua.create_any_userdata(url)?.into_lua(lua)?),
])? ])?
} else { } else {
lua.create_table_from([("tab", self.tab)])? lua.create_table_from([("tab", self.tab.get())])?
} }
.into_lua(lua) .into_lua(lua)
} }

View file

@ -2,26 +2,26 @@ use std::borrow::Cow;
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yazi_shared::fs::Url; use yazi_shared::{Id, fs::Url};
use super::Body; use super::Body;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct BodyHover<'a> { pub struct BodyHover<'a> {
pub tab: usize, pub tab: Id,
pub url: Option<Cow<'a, Url>>, pub url: Option<Cow<'a, Url>>,
} }
impl<'a> BodyHover<'a> { impl<'a> BodyHover<'a> {
#[inline] #[inline]
pub fn borrowed(tab: usize, url: Option<&'a Url>) -> Body<'a> { pub fn borrowed(tab: Id, url: Option<&'a Url>) -> Body<'a> {
Self { tab, url: url.map(Cow::Borrowed) }.into() Self { tab, url: url.map(Cow::Borrowed) }.into()
} }
} }
impl BodyHover<'static> { impl BodyHover<'static> {
#[inline] #[inline]
pub fn dummy(tab: usize) -> Body<'static> { Self { tab, url: None }.into() } pub fn dummy(tab: Id) -> Body<'static> { Self { tab, url: None }.into() }
} }
impl<'a> From<BodyHover<'a>> for Body<'a> { impl<'a> From<BodyHover<'a>> for Body<'a> {
@ -32,11 +32,11 @@ impl IntoLua<'_> for BodyHover<'static> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
if let Some(Cow::Owned(url)) = self.url { if let Some(Cow::Owned(url)) = self.url {
lua.create_table_from([ lua.create_table_from([
("tab", self.tab.into_lua(lua)?), ("tab", self.tab.get().into_lua(lua)?),
("url", lua.create_any_userdata(url)?.into_lua(lua)?), ("url", lua.create_any_userdata(url)?.into_lua(lua)?),
])? ])?
} else { } else {
lua.create_table_from([("tab", self.tab)])? lua.create_table_from([("tab", self.tab.get())])?
} }
.into_lua(lua) .into_lua(lua)
} }

View file

@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
use mlua::Function; use mlua::Function;
use parking_lot::RwLock; use parking_lot::RwLock;
use yazi_boot::BOOT; use yazi_boot::BOOT;
use yazi_shared::{RoCell, fs::Url}; use yazi_shared::{Id, RoCell, fs::Url};
use crate::{Client, ID, PEERS, body::{Body, BodyBulk, BodyCd, BodyDelete, BodyHi, BodyHover, BodyMove, BodyMoveItem, BodyRename, BodyTab, BodyTrash, BodyYank}}; use crate::{Client, ID, PEERS, body::{Body, BodyBulk, BodyCd, BodyDelete, BodyHi, BodyHover, BodyMove, BodyMoveItem, BodyRename, BodyTab, BodyTrash, BodyYank}};
@ -88,7 +88,7 @@ impl Pubsub {
true true
} }
pub fn pub_from_cd(tab: usize, url: &Url) { pub fn pub_from_cd(tab: Id, url: &Url) {
if LOCAL.read().contains_key("cd") { if LOCAL.read().contains_key("cd") {
Self::pub_(BodyCd::dummy(tab)); Self::pub_(BodyCd::dummy(tab));
} }
@ -100,7 +100,7 @@ impl Pubsub {
} }
} }
pub fn pub_from_hover(tab: usize, url: Option<&Url>) { pub fn pub_from_hover(tab: Id, url: Option<&Url>) {
if LOCAL.read().contains_key("hover") { if LOCAL.read().contains_key("hover") {
Self::pub_(BodyHover::dummy(tab)); Self::pub_(BodyHover::dummy(tab));
} }

View file

@ -23,7 +23,7 @@ impl Tab {
pub(super) fn register(lua: &Lua) -> mlua::Result<()> { pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| { lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); reg.add_field_method_get("id", |_, me| Ok(me.id.get()));
reg.add_method("name", |lua, me, ()| { reg.add_method("name", |lua, me, ()| {
lua.create_string(me.current.url.name().as_encoded_bytes()) lua.create_string(me.current.url.name().as_encoded_bytes())
}); });

View file

@ -1,15 +1,15 @@
use std::{collections::{HashMap, HashSet}, mem, ops::Deref, sync::atomic::Ordering}; use std::{collections::{HashMap, HashSet}, mem, ops::Deref};
use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}}; use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}};
use yazi_config::{MANAGER, manager::SortBy}; use yazi_config::{MANAGER, manager::SortBy};
use yazi_shared::fs::{Cha, FILES_TICKET, File, FilesOp, Url, Urn, UrnBuf, maybe_exists}; use yazi_shared::{Id, fs::{Cha, FILES_TICKET, File, FilesOp, Url, Urn, UrnBuf, maybe_exists}};
use super::{FilesSorter, Filter}; use super::{FilesSorter, Filter};
pub struct Files { pub struct Files {
hidden: Vec<File>, hidden: Vec<File>,
items: Vec<File>, items: Vec<File>,
ticket: u64, ticket: Id,
version: u64, version: u64,
pub revision: u64, pub revision: u64,
@ -118,7 +118,7 @@ impl Files {
impl Files { impl Files {
pub fn update_full(&mut self, files: Vec<File>) { pub fn update_full(&mut self, files: Vec<File>) {
self.ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed); self.ticket = FILES_TICKET.next();
(self.hidden, self.items) = self.split_files(files); (self.hidden, self.items) = self.split_files(files);
if !self.items.is_empty() { if !self.items.is_empty() {
@ -126,7 +126,7 @@ impl Files {
} }
} }
pub fn update_part(&mut self, files: Vec<File>, ticket: u64) { pub fn update_part(&mut self, files: Vec<File>, ticket: Id) {
if !files.is_empty() { if !files.is_empty() {
if ticket != self.ticket { if ticket != self.ticket {
return; return;
@ -162,7 +162,7 @@ impl Files {
} }
pub fn update_ioerr(&mut self) { pub fn update_ioerr(&mut self) {
self.ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed); self.ticket = FILES_TICKET.next();
self.hidden.clear(); self.hidden.clear();
self.items.clear(); self.items.clear();
} }
@ -349,7 +349,7 @@ impl Files {
// --- Ticket // --- Ticket
#[inline] #[inline]
pub fn ticket(&self) -> u64 { self.ticket } pub fn ticket(&self) -> Id { self.ticket }
// --- Sorter // --- Sorter
#[inline] #[inline]

View file

@ -1,13 +1,13 @@
use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}}; use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use ratatui::{layout::Rect, text::{Line, Span, Text}}; use ratatui::{layout::Rect, text::{Line, Span, Text}};
use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}};
use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}, sync::OnceCell}; use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}, sync::OnceCell};
use yazi_config::{PREVIEW, THEME, preview::PreviewWrap}; use yazi_config::{PREVIEW, THEME, preview::PreviewWrap};
use yazi_shared::{errors::PeekError, replace_to_printable}; use yazi_shared::{Ids, errors::PeekError, replace_to_printable};
static INCR: AtomicUsize = AtomicUsize::new(0); static INCR: Ids = Ids::new();
static SYNTECT: OnceCell<(Theme, SyntaxSet)> = OnceCell::const_new(); static SYNTECT: OnceCell<(Theme, SyntaxSet)> = OnceCell::const_new();
pub struct Highlighter { pub struct Highlighter {
@ -39,7 +39,7 @@ impl Highlighter {
} }
#[inline] #[inline]
pub fn abort() { INCR.fetch_add(1, Ordering::Relaxed); } pub fn abort() { INCR.next(); }
pub async fn highlight(&self, skip: usize, area: Rect) -> Result<Text<'static>, PeekError> { pub async fn highlight(&self, skip: usize, area: Rect) -> Result<Text<'static>, PeekError> {
let mut reader = BufReader::new(File::open(&self.path).await?); let mut reader = BufReader::new(File::open(&self.path).await?);
@ -104,13 +104,13 @@ impl Highlighter {
after: Vec<String>, after: Vec<String>,
syntax: &'static SyntaxReference, syntax: &'static SyntaxReference,
) -> Result<Text<'static>, PeekError> { ) -> Result<Text<'static>, PeekError> {
let ticket = INCR.load(Ordering::Relaxed); let ticket = INCR.current();
let (theme, syntaxes) = Self::init().await; let (theme, syntaxes) = Self::init().await;
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
let mut h = HighlightLines::new(syntax, theme); let mut h = HighlightLines::new(syntax, theme);
for line in before { for line in before {
if ticket != INCR.load(Ordering::Relaxed) { if ticket != INCR.current() {
return Err("Highlighting cancelled".into()); return Err("Highlighting cancelled".into());
} }
h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?; h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?;
@ -119,7 +119,7 @@ impl Highlighter {
let indent = PREVIEW.indent(); let indent = PREVIEW.indent();
let mut lines = Vec::with_capacity(after.len()); let mut lines = Vec::with_capacity(after.len());
for line in after { for line in after {
if ticket != INCR.load(Ordering::Relaxed) { if ticket != INCR.current() {
return Err("Highlighting cancelled".into()); return Err("Highlighting cancelled".into());
} }

View file

@ -1,5 +1,5 @@
use yazi_macro::emit; use yazi_macro::emit;
use yazi_shared::{Layer, event::Cmd, fs::Url}; use yazi_shared::{Id, Layer, event::Cmd, fs::Url};
use crate::options::OpenDoOpt; use crate::options::OpenDoOpt;
@ -12,7 +12,7 @@ impl ManagerProxy {
} }
#[inline] #[inline]
pub fn hover(url: Option<Url>, tab: usize) { pub fn hover(url: Option<Url>, tab: Id) {
emit!(Call( emit!(Call(
Cmd::args("hover", &url.map_or_else(Vec::new, |u| vec![u])).with("tab", tab), Cmd::args("hover", &url.map_or_else(Vec::new, |u| vec![u])).with("tab", tab),
Layer::Manager Layer::Manager

View file

@ -143,3 +143,5 @@ impl_integer_as!(isize, as_isize);
impl_integer_as!(i16, as_i16); impl_integer_as!(i16, as_i16);
impl_number_as!(f64, as_f64); impl_number_as!(f64, as_f64);
impl_integer_as!(crate::Id, as_id);

View file

@ -1,15 +1,15 @@
use std::{collections::{HashMap, HashSet}, sync::atomic::{AtomicU64, Ordering}}; use std::collections::{HashMap, HashSet};
use super::{Cha, File, UrnBuf}; use super::{Cha, File, UrnBuf};
use crate::{Layer, event::Cmd, fs::Url}; use crate::{Id, Ids, Layer, event::Cmd, fs::Url};
pub static FILES_TICKET: AtomicU64 = AtomicU64::new(0); pub static FILES_TICKET: Ids = Ids::new();
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum FilesOp { pub enum FilesOp {
Full(Url, Vec<File>, Cha), Full(Url, Vec<File>, Cha),
Part(Url, Vec<File>, u64), Part(Url, Vec<File>, Id),
Done(Url, Cha, u64), Done(Url, Cha, Id),
Size(Url, HashMap<UrnBuf, u64>), Size(Url, HashMap<UrnBuf, u64>),
IOErr(Url, std::io::ErrorKind), IOErr(Url, std::io::ErrorKind),
@ -41,8 +41,8 @@ impl FilesOp {
crate::event::Event::Call(Cmd::new("update_files").with_any("op", self), Layer::Manager).emit(); crate::event::Event::Call(Cmd::new("update_files").with_any("op", self), Layer::Manager).emit();
} }
pub fn prepare(cwd: &Url) -> u64 { pub fn prepare(cwd: &Url) -> Id {
let ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed); let ticket = FILES_TICKET.next();
Self::Part(cwd.clone(), vec![], ticket).emit(); Self::Part(cwd.clone(), vec![], ticket).emit();
ticket ticket
} }

54
yazi-shared/src/id.rs Normal file
View file

@ -0,0 +1,54 @@
use std::{fmt::Display, str::FromStr, sync::atomic::{AtomicUsize, Ordering}};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Id(usize);
impl Id {
#[inline]
pub fn get(&self) -> usize { self.0 }
}
impl Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) }
}
impl FromStr for Id {
type Err = <usize as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> { s.parse().map(Self) }
}
impl TryFrom<i64> for Id {
type Error = <usize as TryFrom<i64>>::Error;
fn try_from(value: i64) -> Result<Self, Self::Error> { usize::try_from(value).map(Self) }
}
// --- Ids
pub struct Ids {
next: AtomicUsize,
}
impl Ids {
#[inline]
pub const fn new() -> Self { Self { next: AtomicUsize::new(1) } }
#[inline]
pub fn next(&self) -> Id {
loop {
let old = self.next.fetch_add(1, Ordering::Relaxed);
if old != 0 {
return Id(old);
}
}
}
#[inline]
pub fn current(&self) -> Id { Id(self.next.load(Ordering::Relaxed)) }
}
impl Default for Ids {
fn default() -> Self { Self::new() }
}

View file

@ -2,10 +2,7 @@
yazi_macro::mod_pub!(errors event fs shell theme translit); yazi_macro::mod_pub!(errors event fs shell theme translit);
yazi_macro::mod_flat!( yazi_macro::mod_flat!(chars condition debounce env id layer natsort number os rand ro_cell terminal throttle time xdg);
chars condition debounce env layer natsort number os rand ro_cell terminal throttle
time xdg
);
pub fn init() { pub fn init() {
#[cfg(unix)] #[cfg(unix)]