diff --git a/Cargo.lock b/Cargo.lock index 7ae987c9..129d6628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,9 +389,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.46" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98" +checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6" dependencies = [ "clap", ] diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index da9f6b59..25c44ac7 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -21,8 +21,11 @@ regex = { workspace = true } serde = { workspace = true } [build-dependencies] +yazi-shared = { path = "../yazi-shared", version = "25.3.7" } + +# External dependencies clap = { workspace = true } -clap_complete = "4.5.46" +clap_complete = "4.5.47" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.5.5" vergen-gitcl = { version = "1.0.5", features = [ "build", "rustc" ] } diff --git a/yazi-boot/src/args.rs b/yazi-boot/src/args.rs index 1327c9ca..ee6960d1 100644 --- a/yazi-boot/src/args.rs +++ b/yazi-boot/src/args.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use clap::{Parser, command}; +use yazi_shared::Id; #[derive(Debug, Default, Parser)] #[command(name = "yazi")] @@ -22,7 +23,7 @@ pub struct Args { /// Use the specified client ID, must be a globally unique number #[arg(long)] - pub client_id: Option, + pub client_id: Option, /// Report the specified local events to stdout #[arg(long)] pub local_events: Option, diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 302801dc..a3690a53 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -31,7 +31,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.3.7" } # External build dependencies anyhow = { workspace = true } clap = { workspace = true } -clap_complete = "4.5.46" +clap_complete = "4.5.47" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.5.5" serde_json = { workspace = true } diff --git a/yazi-config/src/popup/input.rs b/yazi-config/src/popup/input.rs index 71c87ba0..f2c05c37 100644 --- a/yazi-config/src/popup/input.rs +++ b/yazi-config/src/popup/input.rs @@ -46,7 +46,7 @@ pub struct Input { } impl Input { - pub const fn border(&self) -> u16 { 1 } + pub const fn border(&self) -> u16 { 2 } } impl FromStr for Input { diff --git a/yazi-dds/src/body/body.rs b/yazi-dds/src/body/body.rs index 2081e3e0..dcb3b721 100644 --- a/yazi-dds/src/body/body.rs +++ b/yazi-dds/src/body/body.rs @@ -1,6 +1,7 @@ use anyhow::{Result, bail}; use mlua::{ExternalResult, IntoLua, Lua, Value}; use serde::Serialize; +use yazi_shared::Id; use super::{BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank}; use crate::Payload; @@ -107,12 +108,12 @@ impl<'a> Body<'a> { } #[inline] - pub fn with_receiver(self, receiver: u64) -> Payload<'a> { + pub fn with_receiver(self, receiver: Id) -> Payload<'a> { Payload::new(self).with_receiver(receiver) } #[inline] - pub fn with_sender(self, sender: u64) -> Payload<'a> { Payload::new(self).with_sender(sender) } + pub fn with_sender(self, sender: Id) -> Payload<'a> { Payload::new(self).with_sender(sender) } } impl IntoLua for Body<'static> { diff --git a/yazi-dds/src/body/hey.rs b/yazi-dds/src/body/hey.rs index 18b55ee0..ca40bdb2 100644 --- a/yazi-dds/src/body/hey.rs +++ b/yazi-dds/src/body/hey.rs @@ -2,19 +2,20 @@ use std::{borrow::Cow, collections::HashMap}; use mlua::{ExternalResult, IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; +use yazi_shared::Id; use super::{Body, BodyHi}; use crate::Peer; #[derive(Debug, Serialize, Deserialize)] pub struct BodyHey { - pub peers: HashMap, + pub peers: HashMap, pub version: Cow<'static, str>, } impl BodyHey { #[inline] - pub fn owned(peers: HashMap) -> Body<'static> { + pub fn owned(peers: HashMap) -> Body<'static> { Self { peers, version: BodyHi::version().into() }.into() } } diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index 9c9d8866..10377710 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -5,19 +5,19 @@ use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time}; use tracing::error; -use yazi_shared::RoCell; +use yazi_shared::{Id, RoCell}; use crate::{ClientReader, ClientWriter, Payload, Pubsub, Server, Stream, body::{Body, BodyBye, BodyHi}}; -pub(super) static ID: RoCell = RoCell::new(); -pub(super) static PEERS: RoCell>> = RoCell::new(); +pub static ID: RoCell = RoCell::new(); +pub(super) static PEERS: RoCell>> = RoCell::new(); pub(super) static QUEUE_TX: RoCell> = RoCell::new(); pub(super) static QUEUE_RX: RoCell> = RoCell::new(); #[derive(Debug)] pub struct Client { - pub(super) id: u64, + pub(super) id: Id, pub(super) tx: mpsc::UnboundedSender, pub(super) abilities: HashSet, } diff --git a/yazi-dds/src/lib.rs b/yazi-dds/src/lib.rs index 3f7acb1a..bc28bc6e 100644 --- a/yazi-dds/src/lib.rs +++ b/yazi-dds/src/lib.rs @@ -8,7 +8,7 @@ pub fn init() { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); // Client - ID.init(yazi_boot::ARGS.client_id.unwrap_or(yazi_shared::timestamp_us())); + ID.init(yazi_boot::ARGS.client_id.unwrap_or(yazi_shared::Id::unique())); PEERS.with(<_>::default); QUEUE_TX.init(tx); QUEUE_RX.init(rx); diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index 1175ac95..321bee61 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -3,19 +3,19 @@ use std::{fmt::Display, io::Write, str::FromStr}; use anyhow::{Result, anyhow}; use yazi_boot::BOOT; use yazi_macro::emit; -use yazi_shared::event::Cmd; +use yazi_shared::{Id, event::Cmd}; use crate::{ID, body::Body}; #[derive(Debug)] pub struct Payload<'a> { - pub receiver: u64, - pub sender: u64, + pub receiver: Id, + pub sender: Id, pub body: Body<'a>, } impl<'a> Payload<'a> { - pub(super) fn new(body: Body<'a>) -> Self { Self { receiver: 0, sender: *ID, body } } + pub(super) fn new(body: Body<'a>) -> Self { Self { receiver: Id(0), sender: *ID, body } } pub(super) fn flush(&self) { writeln!(std::io::stdout(), "{self}").ok(); } @@ -33,12 +33,12 @@ impl<'a> Payload<'a> { } } - pub(super) fn with_receiver(mut self, receiver: u64) -> Self { + pub(super) fn with_receiver(mut self, receiver: Id) -> Self { self.receiver = receiver; self } - pub(super) fn with_sender(mut self, sender: u64) -> Self { + pub(super) fn with_sender(mut self, sender: Id) -> Self { self.sender = sender; self } diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index 2ebf461f..49fd50f8 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -64,7 +64,7 @@ impl Pubsub { pub fn pub_(body: Body<'static>) { body.with_receiver(*ID).emit(); } - pub fn pub_to(receiver: u64, body: Body<'static>) { + pub fn pub_to(receiver: Id, body: Body<'static>) { if receiver == *ID { return Self::pub_(body); } diff --git a/yazi-dds/src/server.rs b/yazi-dds/src/server.rs index 0d0c70d4..ab6bbb9a 100644 --- a/yazi-dds/src/server.rs +++ b/yazi-dds/src/server.rs @@ -3,11 +3,11 @@ use std::{collections::HashMap, str::FromStr, time::Duration}; use anyhow::Result; use parking_lot::RwLock; use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle, time}; -use yazi_shared::RoCell; +use yazi_shared::{Id, RoCell}; use crate::{Client, ClientWriter, Payload, Peer, STATE, Stream, body::{Body, BodyBye, BodyHey}}; -pub(super) static CLIENTS: RoCell>> = RoCell::new(); +pub(super) static CLIENTS: RoCell>> = RoCell::new(); pub(super) struct Server; @@ -87,7 +87,7 @@ impl Server { })) } - fn handle_hi(s: String, id: &mut Option, tx: mpsc::UnboundedSender) { + fn handle_hi(s: String, id: &mut Option, tx: mpsc::UnboundedSender) { let Ok(payload) = Payload::from_str(&s) else { return }; let Body::Hi(hi) = payload.body else { return }; @@ -108,7 +108,7 @@ impl Server { Self::handle_hey(&clients); } - fn handle_hey(clients: &HashMap) { + fn handle_hey(clients: &HashMap) { let payload = format!( "{}\n", Payload::new(BodyHey::owned( @@ -118,7 +118,7 @@ impl Server { clients.values().for_each(|c| _ = c.tx.send(payload.clone())); } - async fn handle_bye(id: u64, mut rx: UnboundedReceiver, mut writer: ClientWriter) { + async fn handle_bye(id: Id, mut rx: UnboundedReceiver, mut writer: ClientWriter) { while let Ok(payload) = rx.try_recv() { if writer.write_all(payload.as_bytes()).await.is_err() { break; @@ -126,7 +126,7 @@ impl Server { } _ = writer - .write_all(BodyBye::owned().with_receiver(id).with_sender(0).to_string().as_bytes()) + .write_all(BodyBye::owned().with_receiver(id).with_sender(Id(0)).to_string().as_bytes()) .await; writer.flush().await.ok(); diff --git a/yazi-plugin/src/id.rs b/yazi-plugin/src/id.rs index 8f14e4d4..db0e48dd 100644 --- a/yazi-plugin/src/id.rs +++ b/yazi-plugin/src/id.rs @@ -1,8 +1,8 @@ use std::ops::Deref; -use mlua::{FromLua, UserData}; +use mlua::{ExternalError, ExternalResult, FromLua, Lua, UserData, Value}; -#[derive(Clone, Copy, FromLua)] +#[derive(Clone, Copy)] pub struct Id(pub yazi_shared::Id); impl Deref for Id { @@ -12,4 +12,18 @@ impl Deref for Id { fn deref(&self) -> &Self::Target { &self.0 } } -impl UserData for Id {} +impl FromLua for Id { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + Ok(match value { + Value::Integer(i) => Self(i.try_into().into_lua_err()?), + Value::UserData(ud) => *ud.borrow::()?, + _ => Err("expected integer or userdata".into_lua_err())?, + }) + } +} + +impl UserData for Id { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("value", |_, me: &Self| Ok(me.0.get())); + } +} diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index 8c6465c8..213f1d3e 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -1,5 +1,6 @@ use mlua::{ExternalResult, Function, Lua, Value}; use yazi_dds::body::Body; +use yazi_shared::Id; use crate::runtime::RtRef; @@ -15,7 +16,7 @@ impl Pubsub { pub(super) fn pub_to(lua: &Lua) -> mlua::Result { lua.create_function(|_, (receiver, kind, value): (u64, mlua::String, Value)| { - yazi_dds::Pubsub::pub_to(receiver, Body::from_lua(&kind.to_str()?, value)?); + yazi_dds::Pubsub::pub_to(Id(receiver), Body::from_lua(&kind.to_str()?, value)?); Ok(()) }) } diff --git a/yazi-plugin/src/utils/app.rs b/yazi-plugin/src/utils/app.rs index dee784b8..0d8f9917 100644 --- a/yazi-plugin/src/utils/app.rs +++ b/yazi-plugin/src/utils/app.rs @@ -8,6 +8,7 @@ impl Utils { pub(super) fn id(lua: &Lua) -> mlua::Result { lua.create_function(|_, type_: mlua::String| { Ok(Id(match type_.as_bytes().as_ref() { + b"app" => *yazi_dds::ID, b"ft" => yazi_fs::FILES_TICKET.next(), _ => Err("Invalid id type".into_lua_err())?, })) diff --git a/yazi-shared/src/id.rs b/yazi-shared/src/id.rs index ab5f1523..3052b575 100644 --- a/yazi-shared/src/id.rs +++ b/yazi-shared/src/id.rs @@ -1,13 +1,15 @@ -use std::{fmt::Display, str::FromStr, sync::atomic::{AtomicUsize, Ordering}}; +use std::{fmt::Display, str::FromStr, sync::atomic::{AtomicU64, Ordering}}; use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)] -pub struct Id(usize); +pub struct Id(pub u64); impl Id { #[inline] - pub fn get(&self) -> usize { self.0 } + pub const fn get(&self) -> u64 { self.0 } + + pub fn unique() -> Self { Id(crate::timestamp_us()) } } impl Display for Id { @@ -15,25 +17,29 @@ impl Display for Id { } impl FromStr for Id { - type Err = ::Err; + type Err = ::Err; fn from_str(s: &str) -> Result { s.parse().map(Self) } } impl TryFrom for Id { - type Error = >::Error; + type Error = >::Error; - fn try_from(value: i64) -> Result { usize::try_from(value).map(Self) } + fn try_from(value: i64) -> Result { u64::try_from(value).map(Self) } +} + +impl PartialEq for Id { + fn eq(&self, other: &u64) -> bool { self.0 == *other } } // --- Ids pub struct Ids { - next: AtomicUsize, + next: AtomicU64, } impl Ids { #[inline] - pub const fn new() -> Self { Self { next: AtomicUsize::new(1) } } + pub const fn new() -> Self { Self { next: AtomicU64::new(1) } } #[inline] pub fn next(&self) -> Id { diff --git a/yazi-widgets/src/input/commands/move_.rs b/yazi-widgets/src/input/commands/move_.rs index 64ff0db2..17d00e64 100644 --- a/yazi-widgets/src/input/commands/move_.rs +++ b/yazi-widgets/src/input/commands/move_.rs @@ -42,8 +42,8 @@ impl Input { let delta = snap.mode.delta(); let s = snap.slice(snap.offset..snap.cursor + delta); if s.width() >= limit { - let s = s.chars().rev().collect::(); - snap.offset = snap.cursor - InputSnap::find_window(&s, 0, limit).end.saturating_sub(delta); + let range = InputSnap::find_window(s.chars().rev(), 0, limit); + snap.offset = snap.cursor - range.end.saturating_sub(delta); } } } diff --git a/yazi-widgets/src/input/snap.rs b/yazi-widgets/src/input/snap.rs index 6b0d09e2..11d39ba9 100644 --- a/yazi-widgets/src/input/snap.rs +++ b/yazi-widgets/src/input/snap.rs @@ -22,19 +22,19 @@ impl InputSnap { op: Default::default(), - mode: Default::default(), + mode: Default::default(), offset: usize::MAX, cursor: usize::MAX, }; - snap.reset(limit); + snap.resize(limit); snap } #[inline] - pub(super) fn reset(&mut self, limit: usize) { + pub(super) fn resize(&mut self, limit: usize) { + let range = Self::find_window(self.value.chars().rev(), 0, limit); self.cursor = self.cursor.min(self.count().saturating_sub(self.mode.delta())); - self.offset = - self.offset.min(self.cursor.saturating_sub(Self::find_window(&self.rev(), 0, limit).end)); + self.offset = self.offset.min(self.cursor.saturating_sub(range.end)); } } @@ -61,30 +61,29 @@ impl InputSnap { &self.value[s.unwrap()..e.unwrap()] } - #[inline] - pub(super) fn rev(&self) -> String { self.value.chars().rev().collect::() } - #[inline] pub(super) fn window(&self, limit: usize) -> Range { - Self::find_window(&self.value, self.offset, limit) + Self::find_window(self.value.chars(), self.offset, limit) } #[inline] - pub(super) fn find_window(s: &str, offset: usize, limit: usize) -> Range { + pub(super) fn find_window(it: T, offset: usize, limit: usize) -> Range + where + T: Iterator, + { let mut width = 0; - let v: Vec<_> = s - .chars() - .enumerate() - .skip(offset) - .map_while(|(i, c)| { - width += c.width().unwrap_or(0); - if width < limit { Some(i) } else { None } - }) - .collect(); + let mut range = None; - if v.is_empty() { - return 0..0; + for (i, c) in it.enumerate().skip(offset) { + width += c.width().unwrap_or(0); + if width > limit { + break; + } + match range { + None => range = Some(i..i + 1), + Some(ref mut r) => r.end = i + 1, + } } - *v.first().unwrap()..v.last().unwrap() + 1 + range.unwrap_or(0..0) } } diff --git a/yazi-widgets/src/input/snaps.rs b/yazi-widgets/src/input/snaps.rs index c0d6b1de..2d19078b 100644 --- a/yazi-widgets/src/input/snaps.rs +++ b/yazi-widgets/src/input/snaps.rs @@ -36,7 +36,7 @@ impl InputSnaps { let value = mem::take(&mut self.versions[self.idx].value); self.versions[self.idx] = self.current.clone(); self.versions[self.idx].value = value; - self.versions[self.idx].reset(limit); + self.versions[self.idx].resize(limit); // If the *current* value is the same as the *last* version if self.versions[self.idx].value == self.current.value { diff --git a/yazi-widgets/src/input/widget.rs b/yazi-widgets/src/input/widget.rs index 10e5ba91..43df8740 100644 --- a/yazi-widgets/src/input/widget.rs +++ b/yazi-widgets/src/input/widget.rs @@ -24,7 +24,7 @@ impl Widget for &Input { height: area.height.min(1), }, THEME.input.selected, - ) + ); } } }