mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: line mode (#291)
This commit is contained in:
parent
51ab86b1b8
commit
ddb5490551
19 changed files with 174 additions and 108 deletions
|
|
@ -127,6 +127,7 @@ impl Executor {
|
||||||
exec.named.contains_key("confirm"),
|
exec.named.contains_key("confirm"),
|
||||||
),
|
),
|
||||||
"hidden" => cx.manager.active_mut().hidden(exec),
|
"hidden" => cx.manager.active_mut().hidden(exec),
|
||||||
|
"linemode" => cx.manager.active_mut().linemode(exec),
|
||||||
"search" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") {
|
"search" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") {
|
||||||
"rg" => cx.manager.active_mut().search(true),
|
"rg" => cx.manager.active_mut().search(true),
|
||||||
"fd" => cx.manager.active_mut().search(false),
|
"fd" => cx.manager.active_mut().search(false),
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,12 @@ keymap = [
|
||||||
{ on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" },
|
{ on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" },
|
||||||
{ on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" },
|
{ on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" },
|
||||||
|
|
||||||
|
# Linemode
|
||||||
|
{ on = [ "m", "s" ], exec = "linemode size", desc = "Set linemode to size" },
|
||||||
|
{ on = [ "m", "p" ], exec = "linemode permissions", desc = "Set linemode to permissions" },
|
||||||
|
{ on = [ "m", "m" ], exec = "linemode mtime", desc = "Set linemode to mtime" },
|
||||||
|
{ on = [ "m", "n" ], exec = "linemode none", desc = "Set linemode to none" },
|
||||||
|
|
||||||
# Copy
|
# Copy
|
||||||
{ on = [ "c", "c" ], exec = "copy path", desc = "Copy the absolute path" },
|
{ on = [ "c", "c" ], exec = "copy path", desc = "Copy the absolute path" },
|
||||||
{ on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" },
|
{ on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" },
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
use anyhow::bail;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
#[serde(try_from = "String")]
|
|
||||||
pub enum Linemode {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Size,
|
|
||||||
Mtime,
|
|
||||||
Permissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<String> for Linemode {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
|
||||||
Ok(match s.as_str() {
|
|
||||||
"none" => Self::None,
|
|
||||||
"size" => Self::Size,
|
|
||||||
"mtime" => Self::Mtime,
|
|
||||||
"permissions" => Self::Permissions,
|
|
||||||
_ => bail!("invalid linemode value: {s}"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use super::{Linemode, ManagerLayout, SortBy};
|
use super::{ManagerLayout, SortBy};
|
||||||
use crate::MERGED_YAZI;
|
use crate::{validation::check_validation, MERGED_YAZI};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Validate)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
pub layout: ManagerLayout,
|
pub layout: ManagerLayout,
|
||||||
|
|
||||||
|
|
@ -14,7 +15,8 @@ pub struct Manager {
|
||||||
pub sort_dir_first: bool,
|
pub sort_dir_first: bool,
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
pub linemode: Linemode,
|
#[validate(length(min = 1, max = 20, message = "must be between 1 and 20 characters"))]
|
||||||
|
pub linemode: String,
|
||||||
pub show_hidden: bool,
|
pub show_hidden: bool,
|
||||||
pub show_symlink: bool,
|
pub show_symlink: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -26,6 +28,9 @@ impl Default for Manager {
|
||||||
manager: Manager,
|
manager: Manager,
|
||||||
}
|
}
|
||||||
|
|
||||||
toml::from_str::<Outer>(&MERGED_YAZI).unwrap().manager
|
let manager = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().manager;
|
||||||
|
|
||||||
|
check_validation(manager.validate());
|
||||||
|
manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
mod layout;
|
mod layout;
|
||||||
mod linemode;
|
|
||||||
mod manager;
|
mod manager;
|
||||||
mod sorting;
|
mod sorting;
|
||||||
|
|
||||||
pub use layout::*;
|
pub use layout::*;
|
||||||
pub use linemode::*;
|
|
||||||
pub use manager::*;
|
pub use manager::*;
|
||||||
pub use sorting::*;
|
pub use sorting::*;
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,17 @@ impl TryFrom<String> for SortBy {
|
||||||
|
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
|
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToString for SortBy {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::None => "none",
|
||||||
|
Self::Alphabetical => "alphabetical",
|
||||||
|
Self::Created => "created",
|
||||||
|
Self::Modified => "modified",
|
||||||
|
Self::Natural => "natural",
|
||||||
|
Self::Size => "size",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use tokio::fs;
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub url: Url,
|
pub url: Url,
|
||||||
pub meta: Metadata,
|
pub meta: Metadata,
|
||||||
pub length: u64,
|
|
||||||
pub(super) link_to: Option<Url>,
|
pub(super) link_to: Option<Url>,
|
||||||
pub is_link: bool,
|
pub is_link: bool,
|
||||||
pub is_hidden: bool,
|
pub is_hidden: bool,
|
||||||
|
|
@ -30,9 +29,8 @@ impl File {
|
||||||
link_to = fs::read_link(&url).await.map(Url::from).ok();
|
link_to = fs::read_link(&url).await.map(Url::from).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = meta.len();
|
|
||||||
let is_hidden = url.file_name().map(|s| s.to_string_lossy().starts_with('.')).unwrap_or(false);
|
let is_hidden = url.file_name().map(|s| s.to_string_lossy().starts_with('.')).unwrap_or(false);
|
||||||
Self { url, meta, length, link_to, is_link, is_hidden }
|
Self { url, meta, link_to, is_link, is_hidden }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ pub struct Files {
|
||||||
ticket: u64,
|
ticket: u64,
|
||||||
pub(crate) version: u64,
|
pub(crate) version: u64,
|
||||||
|
|
||||||
sizes: BTreeMap<Url, u64>,
|
pub sizes: BTreeMap<Url, u64>,
|
||||||
selected: BTreeSet<Url>,
|
selected: BTreeSet<Url>,
|
||||||
|
|
||||||
sorter: FilesSorter,
|
sorter: FilesSorter,
|
||||||
show_hidden: bool,
|
show_hidden: bool,
|
||||||
|
|
@ -268,10 +268,6 @@ impl Files {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn position(&self, url: &Url) -> Option<usize> { self.iter().position(|f| &f.url == url) }
|
pub fn position(&self, url: &Url) -> Option<usize> { self.iter().position(|f| &f.url == url) }
|
||||||
|
|
||||||
// --- Sizes
|
|
||||||
#[inline]
|
|
||||||
pub fn size(&self, url: &Url) -> Option<u64> { self.sizes.get(url).copied() }
|
|
||||||
|
|
||||||
// --- Selected
|
// --- Selected
|
||||||
pub fn selected(&self, pending: &BTreeSet<usize>, unset: bool) -> Vec<&File> {
|
pub fn selected(&self, pending: &BTreeSet<usize>, unset: bool) -> Vec<&File> {
|
||||||
if self.selected.is_empty() && (unset || pending.is_empty()) {
|
if self.selected.is_empty() && (unset || pending.is_empty()) {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ impl FilesSorter {
|
||||||
SortBy::Size => items.sort_unstable_by(|a, b| {
|
SortBy::Size => items.sort_unstable_by(|a, b| {
|
||||||
let aa = if a.is_dir() { sizes.get(&a.url).copied() } else { None };
|
let aa = if a.is_dir() { sizes.get(&a.url).copied() } else { None };
|
||||||
let bb = if b.is_dir() { sizes.get(&b.url).copied() } else { None };
|
let bb = if b.is_dir() { sizes.get(&b.url).copied() } else { None };
|
||||||
self.cmp(aa.unwrap_or(a.length), bb.unwrap_or(b.length), self.promote(a, b))
|
self.cmp(aa.unwrap_or(a.meta.len()), bb.unwrap_or(b.meta.len()), self.promote(a, b))
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -75,7 +75,6 @@ impl FilesSorter {
|
||||||
let dummy = File {
|
let dummy = File {
|
||||||
url: Default::default(),
|
url: Default::default(),
|
||||||
meta: items[0].meta.clone(),
|
meta: items[0].meta.clone(),
|
||||||
length: 0,
|
|
||||||
link_to: None,
|
link_to: None,
|
||||||
is_link: false,
|
is_link: false,
|
||||||
is_hidden: false,
|
is_hidden: false,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ impl Tabs {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tab = Tab::from(url);
|
let mut tab = Tab::from(url);
|
||||||
tab.conf = self.active().conf;
|
tab.conf = self.active().conf.clone();
|
||||||
tab.apply_files_attrs(false);
|
tab.apply_files_attrs(false);
|
||||||
|
|
||||||
self.items.insert(self.idx + 1, tab);
|
self.items.insert(self.idx + 1, tab);
|
||||||
|
|
|
||||||
16
core/src/tab/commands/linemode.rs
Normal file
16
core/src/tab/commands/linemode.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
use config::keymap::Exec;
|
||||||
|
|
||||||
|
use crate::tab::Tab;
|
||||||
|
|
||||||
|
impl Tab {
|
||||||
|
pub fn linemode(&mut self, e: &Exec) -> bool {
|
||||||
|
self.conf.patch(|c| {
|
||||||
|
let Some(mode) = e.args.get(0) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !mode.is_empty() && mode.len() <= 20 {
|
||||||
|
c.linemode = mode.to_owned();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ mod find;
|
||||||
mod hidden;
|
mod hidden;
|
||||||
mod jump;
|
mod jump;
|
||||||
mod leave;
|
mod leave;
|
||||||
|
mod linemode;
|
||||||
mod search;
|
mod search;
|
||||||
mod select;
|
mod select;
|
||||||
mod shell;
|
mod shell;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use config::{manager::{Linemode, SortBy}, MANAGER};
|
use config::{manager::SortBy, MANAGER};
|
||||||
|
|
||||||
use crate::files::FilesSorter;
|
use crate::files::FilesSorter;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
// Sorting
|
// Sorting
|
||||||
pub sort_by: SortBy,
|
pub sort_by: SortBy,
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct Config {
|
||||||
pub sort_dir_first: bool,
|
pub sort_dir_first: bool,
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
pub linemode: Linemode,
|
pub linemode: String,
|
||||||
pub show_hidden: bool,
|
pub show_hidden: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ impl Default for Config {
|
||||||
sort_dir_first: MANAGER.sort_dir_first,
|
sort_dir_first: MANAGER.sort_dir_first,
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
linemode: MANAGER.linemode,
|
linemode: MANAGER.linemode.to_owned(),
|
||||||
show_hidden: MANAGER.show_hidden,
|
show_hidden: MANAGER.show_hidden,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ impl Default for Config {
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub(super) fn patch<F: FnOnce(&mut Self)>(&mut self, f: F) -> bool {
|
pub(super) fn patch<F: FnOnce(&mut Self)>(&mut self, f: F) -> bool {
|
||||||
let old = *self;
|
let old = self.clone();
|
||||||
f(self);
|
f(self);
|
||||||
*self != old
|
*self != old
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ impl Tasks {
|
||||||
|
|
||||||
let targets: Vec<_> = targets
|
let targets: Vec<_> = targets
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|f| f.is_dir() && targets.size(&f.url).is_none())
|
.filter(|f| f.is_dir() && !targets.sizes.contains_key(&f.url))
|
||||||
.map(|f| &f.url)
|
.map(|f| &f.url)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,56 @@ function Folder:by_kind(kind)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Folder:highlighted_name(file)
|
||||||
|
-- Complete prefix when searching across directories
|
||||||
|
local prefix = file:prefix() or ""
|
||||||
|
if prefix ~= "" then
|
||||||
|
prefix = prefix .. "/"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Range highlighting for filenames
|
||||||
|
local highlights = file:highlights()
|
||||||
|
local spans = ui.highlight_ranges(prefix .. file.name, highlights)
|
||||||
|
|
||||||
|
-- Show symlink target
|
||||||
|
if MANAGER.show_symlink and file.link_to ~= nil then
|
||||||
|
spans[#spans + 1] = ui.Span(" -> " .. tostring(file.link_to)):italic()
|
||||||
|
end
|
||||||
|
|
||||||
|
if highlights == nil or not file:is_hovered() then
|
||||||
|
return spans
|
||||||
|
end
|
||||||
|
|
||||||
|
local found = file:found()
|
||||||
|
if found ~= nil then
|
||||||
|
spans[#spans + 1] = ui.Span(" ")
|
||||||
|
spans[#spans + 1] = ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position)
|
||||||
|
end
|
||||||
|
return spans
|
||||||
|
end
|
||||||
|
|
||||||
|
function Folder:linemode(area)
|
||||||
|
local mode = cx.active.conf.linemode
|
||||||
|
if mode == "none" then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
for _, f in ipairs(self:by_kind(self.CURRENT).window) do
|
||||||
|
local spans = { ui.Span(" ") }
|
||||||
|
if mode == "size" then
|
||||||
|
local size = f:size()
|
||||||
|
spans[#spans + 1] = ui.Span(size and utils.readable_size(size) or "")
|
||||||
|
elseif mode == "mtime" then
|
||||||
|
spans[#spans + 1] = ui.Span(os.date("%y-%m-%d %H:%M", f.modified))
|
||||||
|
elseif mode == "permissions" then
|
||||||
|
spans[#spans + 1] = ui.Span(f:permissions() or "")
|
||||||
|
end
|
||||||
|
lines[#lines + 1] = ui.Line(spans)
|
||||||
|
end
|
||||||
|
return ui.Paragraph(area:padding(ui.Padding.right(1)), lines):align(ui.Alignment.RIGHT)
|
||||||
|
end
|
||||||
|
|
||||||
function Folder:markers(area, markers)
|
function Folder:markers(area, markers)
|
||||||
if #markers == 0 then
|
if #markers == 0 then
|
||||||
return {}
|
return {}
|
||||||
|
|
@ -55,34 +105,6 @@ function Folder:markers(area, markers)
|
||||||
return elements
|
return elements
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:highlighted_name(file)
|
|
||||||
-- Complete prefix when searching across directories
|
|
||||||
local prefix = file:prefix() or ""
|
|
||||||
if prefix ~= "" then
|
|
||||||
prefix = prefix .. "/"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Range highlighting for filenames
|
|
||||||
local highlights = file:highlights()
|
|
||||||
local spans = ui.highlight_ranges(prefix .. file.name, highlights)
|
|
||||||
|
|
||||||
-- Show symlink target
|
|
||||||
if MANAGER.show_symlink and file.link_to ~= nil then
|
|
||||||
spans[#spans + 1] = ui.Span(" -> " .. tostring(file.link_to)):italic()
|
|
||||||
end
|
|
||||||
|
|
||||||
if highlights == nil or not file:is_hovered() then
|
|
||||||
return spans
|
|
||||||
end
|
|
||||||
|
|
||||||
local found = file:found()
|
|
||||||
if found ~= nil then
|
|
||||||
spans[#spans + 1] = ui.Span(" ")
|
|
||||||
spans[#spans + 1] = ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position)
|
|
||||||
end
|
|
||||||
return spans
|
|
||||||
end
|
|
||||||
|
|
||||||
function Folder:parent(area)
|
function Folder:parent(area)
|
||||||
local folder = self:by_kind(self.PARENT)
|
local folder = self:by_kind(self.PARENT)
|
||||||
if folder == nil then
|
if folder == nil then
|
||||||
|
|
@ -127,8 +149,7 @@ function Folder:current(area)
|
||||||
markers[#markers + 1] = { i, 3 }
|
markers[#markers + 1] = { i, 3 }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return utils.flat { ui.List(area, items), self:linemode(area), table.unpack(self:markers(area, markers)) }
|
||||||
return { ui.List(area, items), table.unpack(self:markers(area, markers)) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:preview(area)
|
function Folder:preview(area)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ function Status:size()
|
||||||
|
|
||||||
local style = self.style()
|
local style = self.style()
|
||||||
return ui.Line {
|
return ui.Line {
|
||||||
ui.Span(" " .. utils.readable_size(h.length) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg),
|
ui.Span(" " .. utils.readable_size(h:size() or h.length) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg),
|
||||||
ui.Span(THEME.status.separator_close):fg(THEME.status.separator_style.fg),
|
ui.Span(THEME.status.separator_close):fg(THEME.status.separator_style.fg),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -47,13 +47,18 @@ end
|
||||||
|
|
||||||
function Status:permissions()
|
function Status:permissions()
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if h == nil or h.permissions == nil then
|
if h == nil then
|
||||||
|
return ui.Span("")
|
||||||
|
end
|
||||||
|
|
||||||
|
local perm = h:permissions()
|
||||||
|
if perm == nil then
|
||||||
return ui.Span("")
|
return ui.Span("")
|
||||||
end
|
end
|
||||||
|
|
||||||
local spans = {}
|
local spans = {}
|
||||||
for i = 1, #h.permissions do
|
for i = 1, #perm do
|
||||||
local c = h.permissions:sub(i, i)
|
local c = perm:sub(i, i)
|
||||||
local style = THEME.status.permissions_t
|
local style = THEME.status.permissions_t
|
||||||
if c == "-" then
|
if c == "-" then
|
||||||
style = THEME.status.permissions_s
|
style = THEME.status.permissions_s
|
||||||
|
|
@ -109,9 +114,9 @@ function Status:progress(area, offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
local gauge = ui.Gauge(ui.Rect {
|
local gauge = ui.Gauge(ui.Rect {
|
||||||
x = area.x + math.max(0, area.w - offset - 21),
|
x = math.max(0, area.w - offset - 21),
|
||||||
y = area.y,
|
y = area.y,
|
||||||
w = math.min(20, area.w),
|
w = math.max(0, math.min(20, area.w - offset - 1)),
|
||||||
h = 1,
|
h = 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -134,17 +139,12 @@ function Status:progress(area, offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Status:render(area)
|
function Status:render(area)
|
||||||
local chunks = ui.Layout()
|
|
||||||
:direction(ui.Direction.HORIZONTAL)
|
|
||||||
:constraints({ ui.Constraint.Percentage(50), ui.Constraint.Percentage(50) })
|
|
||||||
:split(area)
|
|
||||||
|
|
||||||
local left = ui.Line { self:mode(), self:size(), self:name() }
|
local left = ui.Line { self:mode(), self:size(), self:name() }
|
||||||
local right = ui.Line { self:permissions(), self:percentage(), self:position() }
|
local right = ui.Line { self:permissions(), self:percentage(), self:position() }
|
||||||
local progress = self:progress(chunks[2], right:width())
|
local progress = self:progress(area, right:width())
|
||||||
return {
|
return {
|
||||||
ui.Paragraph(chunks[1], { left }),
|
ui.Paragraph(area, { left }),
|
||||||
ui.Paragraph(chunks[2], { right }):align(ui.Alignment.RIGHT),
|
ui.Paragraph(area, { right }):align(ui.Alignment.RIGHT),
|
||||||
table.unpack(progress),
|
table.unpack(progress),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@ impl<'a, 'b> Active<'a, 'b> {
|
||||||
reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string()));
|
reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string()));
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
LUA.register_userdata_type::<core::tab::Config>(|reg| {
|
||||||
|
reg.add_field_method_get("sort_by", |_, me| Ok(me.sort_by.to_string()));
|
||||||
|
reg.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive));
|
||||||
|
reg.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse));
|
||||||
|
reg.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first));
|
||||||
|
|
||||||
|
reg.add_field_method_get("linemode", |_, me| Ok(me.linemode.to_owned()));
|
||||||
|
reg.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
|
||||||
|
})?;
|
||||||
|
|
||||||
LUA.register_userdata_type::<core::tab::Folder>(|reg| {
|
LUA.register_userdata_type::<core::tab::Folder>(|reg| {
|
||||||
reg.add_field_method_get("cwd", |_, me| Ok(Url::from(&me.cwd)));
|
reg.add_field_method_get("cwd", |_, me| Ok(Url::from(&me.cwd)));
|
||||||
reg.add_field_method_get("offset", |_, me| Ok(me.offset));
|
reg.add_field_method_get("offset", |_, me| Ok(me.offset));
|
||||||
|
|
@ -48,6 +58,7 @@ impl<'a, 'b> Active<'a, 'b> {
|
||||||
pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> {
|
pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> {
|
||||||
let ud = self.scope.create_any_userdata_ref(self.inner)?;
|
let ud = self.scope.create_any_userdata_ref(self.inner)?;
|
||||||
ud.set_named_user_value("mode", self.scope.create_any_userdata_ref(&self.inner.mode)?)?;
|
ud.set_named_user_value("mode", self.scope.create_any_userdata_ref(&self.inner.mode)?)?;
|
||||||
|
ud.set_named_user_value("conf", self.scope.create_any_userdata_ref(&self.inner.conf)?)?;
|
||||||
ud.set_named_user_value(
|
ud.set_named_user_value(
|
||||||
"parent",
|
"parent",
|
||||||
self.inner.parent.as_ref().and_then(|p| self.folder(p, None).ok()),
|
self.inner.parent.as_ref().and_then(|p| self.folder(p, None).ok()),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
use config::THEME;
|
use config::THEME;
|
||||||
use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef};
|
use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef};
|
||||||
|
|
||||||
|
|
@ -13,7 +15,6 @@ impl From<&core::files::File> for File {
|
||||||
impl UserData for File {
|
impl UserData for File {
|
||||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
fields.add_field_method_get("url", |_, me| Ok(Url::from(&me.0.url)));
|
fields.add_field_method_get("url", |_, me| Ok(Url::from(&me.0.url)));
|
||||||
fields.add_field_method_get("length", |_, me| Ok(me.0.length));
|
|
||||||
fields.add_field_method_get("link_to", |_, me| Ok(me.0.link_to().map(Url::from)));
|
fields.add_field_method_get("link_to", |_, me| Ok(me.0.link_to().map(Url::from)));
|
||||||
fields.add_field_method_get("is_link", |_, me| Ok(me.0.is_link));
|
fields.add_field_method_get("is_link", |_, me| Ok(me.0.is_link));
|
||||||
fields.add_field_method_get("is_hidden", |_, me| Ok(me.0.is_hidden));
|
fields.add_field_method_get("is_hidden", |_, me| Ok(me.0.is_hidden));
|
||||||
|
|
@ -48,24 +49,55 @@ impl Files {
|
||||||
|
|
||||||
LUA.register_userdata_type::<core::files::File>(|reg| {
|
LUA.register_userdata_type::<core::files::File>(|reg| {
|
||||||
reg.add_field_method_get("url", |_, me| Ok(Url::from(&me.url)));
|
reg.add_field_method_get("url", |_, me| Ok(Url::from(&me.url)));
|
||||||
reg.add_field_method_get("length", |_, me| Ok(me.length));
|
|
||||||
reg.add_field_method_get("link_to", |_, me| Ok(me.link_to().map(Url::from)));
|
reg.add_field_method_get("link_to", |_, me| Ok(me.link_to().map(Url::from)));
|
||||||
reg.add_field_method_get("is_link", |_, me| Ok(me.is_link));
|
reg.add_field_method_get("is_link", |_, me| Ok(me.is_link));
|
||||||
reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden));
|
reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden));
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
reg.add_field_method_get("is_file", |_, me| Ok(me.is_file()));
|
||||||
|
reg.add_field_method_get("is_dir", |_, me| Ok(me.is_dir()));
|
||||||
|
reg.add_field_method_get("is_symlink", |_, me| Ok(me.meta.is_symlink()));
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::prelude::FileTypeExt;
|
||||||
|
reg.add_field_method_get("is_block_device", |_, me| {
|
||||||
|
Ok(me.meta.file_type().is_block_device())
|
||||||
|
});
|
||||||
|
reg
|
||||||
|
.add_field_method_get("is_char_device", |_, me| Ok(me.meta.file_type().is_char_device()));
|
||||||
|
reg.add_field_method_get("is_fifo", |_, me| Ok(me.meta.file_type().is_fifo()));
|
||||||
|
reg.add_field_method_get("is_socket", |_, me| Ok(me.meta.file_type().is_socket()));
|
||||||
|
}
|
||||||
|
reg.add_field_method_get("length", |_, me| Ok(me.meta.len()));
|
||||||
|
reg.add_field_method_get("created", |_, me| {
|
||||||
|
Ok(me.meta.created()?.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())
|
||||||
|
});
|
||||||
|
reg.add_field_method_get("modified", |_, me| {
|
||||||
|
Ok(me.meta.modified()?.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())
|
||||||
|
});
|
||||||
|
reg.add_field_method_get("accessed", |_, me| {
|
||||||
|
Ok(me.meta.accessed()?.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())
|
||||||
|
});
|
||||||
|
reg.add_method("permissions", |_, me, ()| Ok(shared::permissions(me.meta.permissions())));
|
||||||
|
|
||||||
|
// Extension
|
||||||
reg.add_field_method_get("name", |_, me| {
|
reg.add_field_method_get("name", |_, me| {
|
||||||
Ok(me.url.file_name().map(|n| n.to_string_lossy().to_string()))
|
Ok(me.url.file_name().map(|n| n.to_string_lossy().to_string()))
|
||||||
});
|
});
|
||||||
reg.add_field_method_get("permissions", |_, me| {
|
reg.add_function("size", |_, me: AnyUserData| {
|
||||||
Ok(shared::permissions(me.meta.permissions()))
|
let file = me.borrow::<core::files::File>()?;
|
||||||
});
|
if !file.is_dir() {
|
||||||
|
return Ok(Some(file.meta.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
||||||
|
Ok(folder.files.sizes.get(&file.url).copied())
|
||||||
|
});
|
||||||
reg.add_function("mime", |_, me: AnyUserData| {
|
reg.add_function("mime", |_, me: AnyUserData| {
|
||||||
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
||||||
let file = me.borrow::<core::files::File>()?;
|
let file = me.borrow::<core::files::File>()?;
|
||||||
Ok(manager.mimetype.get(&file.url).cloned())
|
Ok(manager.mimetype.get(&file.url).cloned())
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("prefix", |_, me: AnyUserData| {
|
reg.add_function("prefix", |_, me: AnyUserData| {
|
||||||
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
||||||
if !folder.cwd.is_search() {
|
if !folder.cwd.is_search() {
|
||||||
|
|
@ -77,7 +109,6 @@ impl Files {
|
||||||
p.next_back();
|
p.next_back();
|
||||||
Ok(Some(p.as_path().to_string_lossy().to_string()))
|
Ok(Some(p.as_path().to_string_lossy().to_string()))
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_method("icon", |_, me, ()| {
|
reg.add_method("icon", |_, me, ()| {
|
||||||
Ok(
|
Ok(
|
||||||
THEME
|
THEME
|
||||||
|
|
@ -87,7 +118,6 @@ impl Files {
|
||||||
.map(|x| x.display.to_string()),
|
.map(|x| x.display.to_string()),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("style", |_, me: AnyUserData| {
|
reg.add_function("style", |_, me: AnyUserData| {
|
||||||
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
||||||
let file = me.borrow::<core::files::File>()?;
|
let file = me.borrow::<core::files::File>()?;
|
||||||
|
|
@ -100,13 +130,11 @@ impl Files {
|
||||||
.map(|x| Style::from(x.style)),
|
.map(|x| Style::from(x.style)),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("is_hovered", |_, me: AnyUserData| {
|
reg.add_function("is_hovered", |_, me: AnyUserData| {
|
||||||
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
||||||
let file = me.borrow::<core::files::File>()?;
|
let file = me.borrow::<core::files::File>()?;
|
||||||
Ok(matches!(folder.hovered(), Some(f) if f.url == file.url))
|
Ok(matches!(folder.hovered(), Some(f) if f.url == file.url))
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("is_yanked", |_, me: AnyUserData| {
|
reg.add_function("is_yanked", |_, me: AnyUserData| {
|
||||||
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
||||||
let file = me.borrow::<core::files::File>()?;
|
let file = me.borrow::<core::files::File>()?;
|
||||||
|
|
@ -118,7 +146,6 @@ impl Files {
|
||||||
1u8
|
1u8
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("is_selected", |_, me: AnyUserData| {
|
reg.add_function("is_selected", |_, me: AnyUserData| {
|
||||||
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
||||||
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
let folder = me.named_user_value::<UserDataRef<core::tab::Folder>>("folder")?;
|
||||||
|
|
@ -132,7 +159,6 @@ impl Files {
|
||||||
manager.active().mode.pending(folder.offset + idx, selected)
|
manager.active().mode.pending(folder.offset + idx, selected)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("found", |lua, me: AnyUserData| {
|
reg.add_function("found", |lua, me: AnyUserData| {
|
||||||
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
||||||
let Some(finder) = &manager.active().finder else {
|
let Some(finder) = &manager.active().finder else {
|
||||||
|
|
@ -148,7 +174,6 @@ impl Files {
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_function("highlights", |_, me: AnyUserData| {
|
reg.add_function("highlights", |_, me: AnyUserData| {
|
||||||
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
let manager = me.named_user_value::<UserDataRef<core::manager::Manager>>("manager")?;
|
||||||
let Some(finder) = &manager.active().finder else {
|
let Some(finder) = &manager.active().finder else {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ impl<'a, 'b> Tabs<'a, 'b> {
|
||||||
});
|
});
|
||||||
|
|
||||||
reg.add_field_function_get("mode", |_, me| me.named_user_value::<AnyUserData>("mode"));
|
reg.add_field_function_get("mode", |_, me| me.named_user_value::<AnyUserData>("mode"));
|
||||||
|
reg.add_field_function_get("conf", |_, me| me.named_user_value::<AnyUserData>("conf"));
|
||||||
reg.add_field_function_get("parent", |_, me| me.named_user_value::<Value>("parent"));
|
reg.add_field_function_get("parent", |_, me| me.named_user_value::<Value>("parent"));
|
||||||
reg.add_field_function_get("current", |_, me| me.named_user_value::<AnyUserData>("current"));
|
reg.add_field_function_get("current", |_, me| me.named_user_value::<AnyUserData>("current"));
|
||||||
reg.add_field_function_get("preview", |_, me| me.named_user_value::<AnyUserData>("preview"));
|
reg.add_field_function_get("preview", |_, me| me.named_user_value::<AnyUserData>("preview"));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue