feat: show file extension for truncated file list items (#2759)

This commit is contained in:
三咲雅 · Misaki Masa 2025-05-15 14:34:10 +08:00 committed by GitHub
parent 9a0ef6bb20
commit 464acfe47b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 122 additions and 44 deletions

36
Cargo.lock generated
View file

@ -252,9 +252,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bitstream-io"
@ -572,7 +572,7 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"crossterm_winapi",
"mio",
"parking_lot",
@ -588,7 +588,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"crossterm_winapi",
"derive_more",
"document-features",
@ -1163,7 +1163,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"inotify-sys",
"libc",
]
@ -1305,7 +1305,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"libc",
"redox_syscall",
]
@ -1527,7 +1527,7 @@ version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"filetime",
"fsevent-sys",
"inotify",
@ -1658,7 +1658,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"block2",
"libc",
"objc2",
@ -1998,7 +1998,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"cassowary",
"compact_str",
"crossterm 0.28.1",
@ -2078,7 +2078,7 @@ version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
]
[[package]]
@ -2169,7 +2169,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"errno",
"libc",
"linux-raw-sys 0.4.15",
@ -2182,7 +2182,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"errno",
"libc",
"linux-raw-sys 0.9.4",
@ -3006,9 +3006,9 @@ dependencies = [
[[package]]
name = "weezl"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
checksum = "78c888f2a25014723b17ac60a626d626549f773e0d702b1320956f8d769ae1f8"
[[package]]
name = "which"
@ -3278,7 +3278,7 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
]
[[package]]
@ -3377,7 +3377,7 @@ name = "yazi-config"
version = "25.5.14"
dependencies = [
"anyhow",
"bitflags 2.9.0",
"bitflags 2.9.1",
"crossterm 0.29.0",
"dirs",
"globset",
@ -3399,7 +3399,7 @@ name = "yazi-core"
version = "25.5.14"
dependencies = [
"anyhow",
"bitflags 2.9.0",
"bitflags 2.9.1",
"crossterm 0.29.0",
"dirs",
"futures",
@ -3509,7 +3509,7 @@ version = "25.5.14"
dependencies = [
"anyhow",
"arc-swap",
"bitflags 2.9.0",
"bitflags 2.9.1",
"core-foundation-sys",
"dirs",
"foldhash",

View file

@ -23,7 +23,7 @@ lto = false
ansi-to-tui = "7.0.0"
anyhow = "1.0.98"
base64 = "0.22.1"
bitflags = "2.9.0"
bitflags = "2.9.1"
clap = { version = "4.5.38", features = [ "derive" ] }
core-foundation-sys = "0.8.7"
crossterm = { version = "0.29.0", features = [ "event-stream" ] }

View file

@ -34,9 +34,11 @@ function Current:redraw()
local left, right = {}, {}
for _, f in ipairs(files) do
left[#left + 1] = Entity:new(f):redraw()
right[#right + 1] = Linemode:new(f):redraw()
left[#left]:truncate { max = math.max(0, self._area.w - right[#right]:width()) }
local entity = Entity:new(f)
left[#left + 1], right[#right + 1] = entity:redraw(), Linemode:new(f):redraw()
local max = math.max(0, self._area.w - right[#right]:width())
left[#left]:truncate { max = max, ellipsis = entity:ellipsis(max) }
end
return {

View file

@ -79,7 +79,8 @@ end
function Entity:redraw()
local lines = {}
for _, c in ipairs(self._children) do
lines[#lines + 1] = (type(c[1]) == "string" and self[c[1]] or c[1])(self)
local line = ui.Line((type(c[1]) == "string" and self[c[1]] or c[1])(self))
c.width, lines[#lines + 1] = line:width(), line
end
return ui.Line(lines):style(self:style())
end
@ -95,6 +96,19 @@ function Entity:style()
end
end
function Entity:ellipsis(max)
local adv, f = 0, self._file
for _, child in ipairs(self._children) do
adv = adv + child.width
if adv > max then
return not f.cha.is_dir and f.url.ext and "…." .. f.url.ext or ""
elseif child.id == 4 then
break
end
end
return ""
end
-- Children
function Entity:children_add(fn, order)
self._inc = self._inc + 1

View file

@ -19,7 +19,11 @@ function Parent:redraw()
local items = {}
for _, f in ipairs(self._folder.window) do
items[#items + 1] = Entity:new(f):redraw():truncate { max = self._area.w }
local entity = Entity:new(f)
items[#items + 1] = entity:redraw():truncate {
max = self._area.w,
ellipsis = entity:ellipsis(self._area.w),
}
end
return {

View file

@ -21,7 +21,11 @@ function M:peek(job)
local items = {}
for _, f in ipairs(folder.window) do
items[#items + 1] = Entity:new(f):redraw():truncate { max = job.area.w }
local entity = Entity:new(f)
items[#items + 1] = entity:redraw():truncate {
max = job.area.w,
ellipsis = entity:ellipsis(job.area.w),
}
end
ya.preview_widget(job, {

View file

@ -133,27 +133,46 @@ impl UserData for Line {
});
methods.add_function_mut("truncate", |_, (ud, t): (AnyUserData, Table)| {
let mut me = ud.borrow_mut::<Self>()?;
let max = t.raw_get("max")?;
let mut width = 0;
let max = t.raw_get("max")?;
if max < 1 {
me.inner.spans.clear();
return Ok(ud);
}
let ellipsis = match t.raw_get::<Value>("ellipsis")? {
Value::Nil => (1, ratatui::text::Span::raw("")),
v => {
let mut span = Span::try_from(v)?;
(span.truncate(max), span.0)
}
};
let (mut width, mut last) = (0, None);
'outer: for (x, span) in me.inner.iter_mut().enumerate() {
for (y, c) in span.content.char_indices() {
width += c.width().unwrap_or(0);
if width < max {
continue;
} else if width == max && span.content[y..].chars().nth(1).is_none() {
continue;
match last {
None if width > max - ellipsis.0 => last = Some((false, x, y)),
Some((false, x, y)) if width > max => {
last = Some((true, x, y));
break 'outer;
}
_ => {}
}
match &mut span.content {
Cow::Borrowed(s) => span.content = Cow::Borrowed(&s[..y]),
Cow::Owned(s) => s.truncate(y),
}
me.inner.spans.truncate(x + 1);
me.inner.spans.push(ratatui::text::Span::raw(""));
break 'outer;
}
}
if let Some((true, x, y)) = last {
let spans = &mut me.inner.spans;
match &mut spans[x].content {
Cow::Borrowed(s) => spans[x].content = Cow::Borrowed(&s[..y]),
Cow::Owned(s) => s.truncate(y),
}
spans.truncate(x + 1);
spans.push(ellipsis.1);
}
Ok(ud)
});
}

View file

@ -1,4 +1,6 @@
use mlua::{ExternalError, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use std::borrow::Cow;
use mlua::{AnyUserData, ExternalError, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use unicode_width::UnicodeWidthChar;
const EXPECTED: &str = "expected a string or Span";
@ -14,6 +16,36 @@ impl Span {
Ok(span)
}
pub(super) fn truncate(&mut self, max: usize) -> usize {
if max < 1 {
match &mut self.0.content {
Cow::Borrowed(_) => self.0.content = Cow::Borrowed(""),
Cow::Owned(s) => s.clear(),
}
return 0;
}
let mut adv = 0;
let mut last;
for (i, c) in self.0.content.char_indices() {
(last, adv) = (adv, adv + c.width().unwrap_or(0));
if adv < max {
continue;
} else if adv == max && self.0.content[i..].chars().nth(1).is_none() {
return max;
}
match &mut self.0.content {
Cow::Borrowed(s) => self.0.content = format!("{}", &s[..i]).into(),
Cow::Owned(s) => {
s.truncate(i);
s.push('…');
}
}
return last + 1;
}
adv
}
}
impl TryFrom<Value> for Span {
@ -42,5 +74,9 @@ impl UserData for Span {
methods.add_method("visible", |_, Span(me), ()| {
Ok(me.content.chars().any(|c| c.width().unwrap_or(0) > 0))
});
methods.add_function_mut("truncate", |_, (ud, t): (AnyUserData, Table)| {
ud.borrow_mut::<Self>()?.truncate(t.raw_get("max")?);
Ok(ud)
});
}
}

View file

@ -68,10 +68,9 @@ impl Utils {
pub(super) fn confirm(lua: &Lua) -> mlua::Result<Function> {
fn content(t: &Table) -> mlua::Result<ratatui::widgets::Paragraph<'static>> {
Ok(match t.raw_get::<Value>("content") {
Ok(v) if v.is_nil() => Default::default(),
Ok(v) => Text::try_from(v)?.into(),
Err(e) => Err(e)?,
Ok(match t.raw_get::<Value>("content")? {
Value::Nil => Default::default(),
v => Text::try_from(v)?.into(),
})
}