Add colorful permissions

Ref: https://github.com/sayanarijit/xplr/issues/187
pull/189/head
Arijit Basu 3 years ago committed by Arijit Basu
parent 91838f88ce
commit cb695fcaa7

17
Cargo.lock generated

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ansi-to-tui"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5091b918c2bf8daa9031e1cef129eafc45272352b885f114fb36a9aec512fcf2"
dependencies = [
"tui",
]
[[package]]
name = "anyhow"
version = "1.0.40"
@ -410,9 +419,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.92"
version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "linked-hash-map"
@ -1099,8 +1108,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xplr"
version = "0.10.2"
version = "0.11.0"
dependencies = [
"ansi-to-tui",
"anyhow",
"chrono",
"criterion",
@ -1109,6 +1119,7 @@ dependencies = [
"humansize",
"indexmap",
"lazy_static",
"libc",
"mime_guess",
"mlua",
"natord",

@ -1,6 +1,6 @@
[package]
name = "xplr"
version = "0.10.2" # Update lua.rs
version = "0.11.0" # Update lua.rs
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
description = "A hackable, minimal, fast TUI file explorer"
@ -29,6 +29,8 @@ indexmap = { version = "1.6.2", features = ["serde"] }
natord = "1.0.9"
humansize = "1.1.0"
mlua = { version = "0.5.4", features = ["luajit", "vendored", "serialize", "send"] }
ansi-to-tui = "0.1.9"
libc = "0.2.94"
[dev-dependencies]
criterion = "0.3"

@ -3,6 +3,7 @@ use crate::config::Mode;
use crate::explorer;
use crate::input::Key;
use crate::lua;
use crate::permissions::Permissions;
use crate::ui::Layout;
use anyhow::{bail, Result};
use chrono::{DateTime, Local};
@ -212,6 +213,7 @@ pub struct Node {
pub mime_essence: String,
pub size: u64,
pub human_size: String,
pub permissions: Permissions,
pub canonical: Option<ResolvedNode>,
pub symlink: Option<ResolvedNode>,
}
@ -235,7 +237,7 @@ impl Node {
.map(|p| (false, Some(ResolvedNode::from(p))))
.unwrap_or_else(|_| (true, None));
let (is_symlink, is_dir, is_file, is_readonly, size) = path
let (is_symlink, is_dir, is_file, is_readonly, size, permissions) = path
.symlink_metadata()
.map(|m| {
(
@ -244,9 +246,10 @@ impl Node {
m.is_file(),
m.permissions().readonly(),
m.len(),
Permissions::from(&m),
)
})
.unwrap_or((false, false, false, false, 0));
.unwrap_or_else(|_| (false, false, false, false, 0, Permissions::default()));
let mime_essence = mime_guess::from_path(&path)
.first()
@ -268,6 +271,7 @@ impl Node {
mime_essence,
size,
human_size,
permissions,
canonical: maybe_canonical_meta.clone(),
symlink: if is_symlink {
maybe_canonical_meta
@ -346,6 +350,11 @@ impl Node {
pub fn human_size(&self) -> &String {
&self.human_size
}
/// Get a reference to the node's permissions.
pub fn permissions(&self) -> &Permissions {
&self.permissions
}
}
impl Ord for Node {

@ -270,7 +270,8 @@ xplr.config.general.table.col_spacing = 1
xplr.config.general.table.col_widths = {
{ Percentage = 10 },
{ Percentage = 50 },
{ Percentage = 20 },
{ Percentage = 10 },
{ Percentage = 10 },
{ Percentage = 20 },
}
@ -278,6 +279,7 @@ xplr.config.general.table.col_widths = {
xplr.config.general.table.header.cols = {
{ format = " index", style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil } },
{ format = "╭──── path", style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil } },
{ format = "permissions", style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil } },
{ format = "size", style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil } },
{ format = "type", style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil } },
}
@ -305,6 +307,10 @@ xplr.config.general.table.row.cols = {
format = "builtin.fmt_general_table_row_cols_3",
style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil }
},
{
format = "builtin.fmt_general_table_row_cols_4",
style = { add_modifiers = nil, bg = nil, fg = nil, sub_modifiers = nil }
},
}
xplr.config.general.table.row.height = 0
xplr.config.general.table.row.style.add_modifiers = nil
@ -1999,9 +2005,9 @@ xplr.config.modes.builtin.switch_layout = {
---- Custom
xplr.config.modes.custom = {}
-- Function
---- Formaters
------ Index
xplr.fn.builtin.fmt_general_table_row_cols_0 = function(m)
local r = ""
if m.is_before_focus then
@ -2015,11 +2021,12 @@ xplr.fn.builtin.fmt_general_table_row_cols_0 = function(m)
return r
end
------ Path
xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
local r = m.tree .. m.prefix
if m.meta.icon == nil then
r = r .. " "
r = r .. " "
else
r = r .. m.meta.icon .. " "
end
@ -2044,13 +2051,70 @@ xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
r = r .. "/"
end
end
end
return r
end
------ Permissions
xplr.fn.builtin.fmt_general_table_row_cols_2 = function(m)
local no_color = os.getenv("NO_COLOR")
local function green(x)
if no_color == nil then
return "\x1b[32m" .. x .. "\x1b[0m"
else
return x
end
end
local function yellow(x)
if no_color == nil then
return "\x1b[33m" .. x .. "\x1b[0m"
else
return x
end
end
local function red(x)
if no_color == nil then
return "\x1b[31m" .. x .. "\x1b[0m"
else
return x
end
end
local function bit(x, color, cond)
if cond then
return color(x)
else
return color("-")
end
end
local r = ""
-- User
r = r .. bit("r", green, m.permissions.user_read)
r = r .. bit("w", yellow, m.permissions.user_write)
r = r .. bit("x", red, m.permissions.user_execute)
-- Group
r = r .. bit("r", green, m.permissions.group_read)
r = r .. bit("w", yellow, m.permissions.group_write)
r = r .. bit("x", red, m.permissions.group_execute)
-- Other
r = r .. bit("r", green, m.permissions.other_read)
r = r .. bit("w", yellow, m.permissions.other_write)
r = r .. bit("x", red, m.permissions.other_execute)
return r
end
------ Size
xplr.fn.builtin.fmt_general_table_row_cols_3 = function(m)
if not m.is_dir then
return m.human_size
else
@ -2058,7 +2122,8 @@ xplr.fn.builtin.fmt_general_table_row_cols_2 = function(m)
end
end
xplr.fn.builtin.fmt_general_table_row_cols_3 = function(m)
------ Mime
xplr.fn.builtin.fmt_general_table_row_cols_4 = function(m)
if m.is_symlink and not m.is_broken then
return m.symlink.mime_essence
else

@ -9,6 +9,7 @@ pub mod event_reader;
pub mod explorer;
pub mod input;
pub mod lua;
pub mod permissions;
pub mod pipe_reader;
pub mod pwd_watcher;
pub mod runner;

@ -133,12 +133,10 @@ mod test {
#[test]
fn test_compatibility() {
assert!(check_version(VERSION, "foo path").is_ok());
assert!(check_version("0.10.0", "foo path").is_ok());
assert!(check_version("0.10.1", "foo path").is_ok());
assert!(check_version("0.10.2", "foo path").is_ok());
assert!(check_version("0.11.0", "foo path").is_ok());
assert!(check_version("0.10.3", "foo path").is_err());
assert!(check_version("0.9.1", "foo path").is_err());
assert!(check_version("1.10.1", "foo path").is_err());
assert!(check_version("0.11.1", "foo path").is_err());
assert!(check_version("0.10.0", "foo path").is_err());
assert!(check_version("1.12.0", "foo path").is_err());
}
}

@ -0,0 +1,81 @@
// Stolen from https://github.com/Peltoche/lsd/blob/master/src/meta/permissions.rs
use serde::{Deserialize, Serialize};
use std::fs::Metadata;
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Hash, Default)]
pub struct Permissions {
pub user_read: bool,
pub user_write: bool,
pub user_execute: bool,
pub group_read: bool,
pub group_write: bool,
pub group_execute: bool,
pub other_read: bool,
pub other_write: bool,
pub other_execute: bool,
pub sticky: bool,
pub setgid: bool,
pub setuid: bool,
}
impl<'a> From<&'a Metadata> for Permissions {
#[cfg(unix)]
fn from(meta: &Metadata) -> Self {
use std::os::unix::fs::PermissionsExt;
let bits = meta.permissions().mode();
let has_bit = |bit| bits & bit == bit;
Self {
user_read: has_bit(modes::USER_READ),
user_write: has_bit(modes::USER_WRITE),
user_execute: has_bit(modes::USER_EXECUTE),
group_read: has_bit(modes::GROUP_READ),
group_write: has_bit(modes::GROUP_WRITE),
group_execute: has_bit(modes::GROUP_EXECUTE),
other_read: has_bit(modes::OTHER_READ),
other_write: has_bit(modes::OTHER_WRITE),
other_execute: has_bit(modes::OTHER_EXECUTE),
sticky: has_bit(modes::STICKY),
setgid: has_bit(modes::SETGID),
setuid: has_bit(modes::SETUID),
}
}
#[cfg(windows)]
fn from(_: &Metadata) -> Self {
panic!("Cannot get permissions from metadata on Windows")
}
}
// More readable aliases for the permission bits exposed by libc.
#[allow(trivial_numeric_casts)]
#[cfg(unix)]
mod modes {
pub type Mode = u32;
// The `libc::mode_t` types actual type varies, but the value returned
// from `metadata.permissions().mode()` is always `u32`.
pub const USER_READ: Mode = libc::S_IRUSR as Mode;
pub const USER_WRITE: Mode = libc::S_IWUSR as Mode;
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
pub const GROUP_READ: Mode = libc::S_IRGRP as Mode;
pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode;
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
pub const OTHER_READ: Mode = libc::S_IROTH as Mode;
pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode;
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
pub const STICKY: Mode = libc::S_ISVTX as Mode;
pub const SETGID: Mode = libc::S_ISGID as Mode;
pub const SETUID: Mode = libc::S_ISUID as Mode;
}

@ -3,6 +3,9 @@ use crate::app::HelpMenuLine;
use crate::app::{Node, ResolvedNode};
use crate::config::PanelUiConfig;
use crate::lua;
use crate::permissions::Permissions;
use ansi_to_tui::ansi_to_text;
use anyhow::Result;
use indexmap::IndexSet;
use lazy_static::lazy_static;
use mlua::Lua;
@ -15,7 +18,7 @@ use tui::backend::Backend;
use tui::layout::Rect;
use tui::layout::{Constraint as TuiConstraint, Direction, Layout as TuiLayout};
use tui::style::{Color, Modifier as TuiModifier, Style as TuiStyle};
use tui::text::{Span, Spans};
use tui::text::{Span, Spans, Text};
use tui::widgets::{Block, Borders as TuiBorders, Cell, List, ListItem, Paragraph, Row, Table};
use tui::Frame;
@ -319,7 +322,7 @@ impl From<ResolvedNode> for ResolvedNodeUiMetadata {
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct NodeUiMetadata {
pub struct NodeUiMetadata {
// From Node
pub parent: String,
pub relative_path: String,
@ -333,6 +336,7 @@ struct NodeUiMetadata {
pub mime_essence: String,
pub size: u64,
pub human_size: String,
pub permissions: Permissions,
pub canonical: Option<ResolvedNodeUiMetadata>,
pub symlink: Option<ResolvedNodeUiMetadata>,
@ -378,6 +382,7 @@ impl NodeUiMetadata {
mime_essence: node.mime_essence().clone(),
size: node.size(),
human_size: node.human_size().clone(),
permissions: node.permissions().to_owned(),
canonical: node.canonical().to_owned().map(|s| s.into()),
symlink: node.symlink().to_owned().map(|s| s.into()),
index,
@ -541,10 +546,27 @@ fn draw_table<B: Backend>(
.iter()
.filter_map(|c| {
c.format().as_ref().map(|f| {
lua::call(lua, f, &v).unwrap_or_else(|e| e.to_string())
let out: Result<String> = lua::call(lua, f, &v);
match out {
Ok(o) => {
let text =
ansi_to_text(o.bytes()).unwrap_or_else(|e| {
Text::raw(format!("{:?}", e))
});
// TODO: Track https://github.com/uttarayan21/ansi-to-tui/issues/2
// And https://github.com/fdehau/tui-rs/issues/304
if text.lines.is_empty() {
Text::raw(o.to_string())
} else {
text
}
}
Err(e) => Text::raw(e.to_string()),
}
})
})
.collect::<Vec<String>>()
.collect::<Vec<Text>>()
})
.unwrap_or_default()
.iter()

Loading…
Cancel
Save