Added inode size

Also supports sorting by inode size.

Closes: https://github.com/sayanarijit/xplr/issues/84
pull/89/head
Arijit Basu 3 years ago committed by Arijit Basu
parent b53f0c21bb
commit ca13ebb193

7
Cargo.lock generated

@ -552,6 +552,12 @@ dependencies = [
"libc",
]
[[package]]
name = "humansize"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
[[package]]
name = "ident_case"
version = "1.0.1"
@ -1632,6 +1638,7 @@ dependencies = [
"crossterm",
"dirs",
"handlebars",
"humansize",
"indexmap",
"lazy_static",
"mime_guess",

@ -26,6 +26,7 @@ notify = "4.0.12"
lazy_static = "1.4.0"
indexmap = { version = "1.6.2", features = ["serde"] }
natord = "1.0.9"
humansize = "1.1.0"
[dev-dependencies]
criterion = "0.3"

@ -91,6 +91,7 @@ pub struct ResolvedNode {
pub is_file: bool,
pub is_readonly: bool,
pub mime_essence: String,
pub size: u64,
}
impl ResolvedNode {
@ -100,15 +101,10 @@ impl ResolvedNode {
.map(|e| e.to_string_lossy().to_string())
.unwrap_or_default();
let maybe_metadata = path.metadata().ok();
let is_dir = maybe_metadata.clone().map(|m| m.is_dir()).unwrap_or(false);
let is_file = maybe_metadata.clone().map(|m| m.is_file()).unwrap_or(false);
let is_readonly = maybe_metadata
.map(|m| m.permissions().readonly())
.unwrap_or(false);
let (is_dir, is_file, is_readonly, size) = path
.metadata()
.map(|m| (m.is_dir(), m.is_file(), m.permissions().readonly(), m.len()))
.unwrap_or((false, false, false, 0));
let mime_essence = mime_guess::from_path(&path)
.first()
@ -122,6 +118,7 @@ impl ResolvedNode {
is_file,
is_readonly,
mime_essence,
size,
}
}
}
@ -138,6 +135,7 @@ pub struct Node {
pub is_broken: bool,
pub is_readonly: bool,
pub mime_essence: String,
pub size: u64,
pub canonical: Option<ResolvedNode>,
pub symlink: Option<ResolvedNode>,
}
@ -156,25 +154,23 @@ impl Node {
.map(|e| e.to_string_lossy().to_string())
.unwrap_or_default();
let maybe_metadata = path.symlink_metadata().ok();
let is_symlink = maybe_metadata
.clone()
.map(|m| m.file_type().is_symlink())
.unwrap_or(false);
let (is_broken, maybe_canonical_meta) = path
.canonicalize()
.map(|p| (false, Some(ResolvedNode::from(p))))
.unwrap_or_else(|_| (true, None));
let is_dir = maybe_metadata.clone().map(|m| m.is_dir()).unwrap_or(false);
let is_file = maybe_metadata.clone().map(|m| m.is_file()).unwrap_or(false);
let is_readonly = maybe_metadata
.map(|m| m.permissions().readonly())
.unwrap_or(false);
let (is_symlink, is_dir, is_file, is_readonly, size) = path
.symlink_metadata()
.map(|m| {
(
m.file_type().is_symlink(),
m.is_dir(),
m.is_file(),
m.permissions().readonly(),
m.len(),
)
})
.unwrap_or((false, false, false, false, 0));
let mime_essence = mime_guess::from_path(&path)
.first()
@ -192,6 +188,7 @@ impl Node {
is_broken,
is_readonly,
mime_essence,
size,
canonical: maybe_canonical_meta.clone(),
symlink: if is_symlink {
maybe_canonical_meta
@ -258,6 +255,7 @@ pub enum NodeSorter {
ByIsBroken,
ByIsReadonly,
ByMimeEssence,
BySize,
ByCanonicalAbsolutePath,
ByICanonicalAbsolutePath,
@ -266,6 +264,7 @@ pub enum NodeSorter {
ByCanonicalIsFile,
ByCanonicalIsReadonly,
ByCanonicalMimeEssence,
ByCanonicalSize,
BySymlinkAbsolutePath,
ByISymlinkAbsolutePath,
@ -274,6 +273,7 @@ pub enum NodeSorter {
BySymlinkIsFile,
BySymlinkIsReadonly,
BySymlinkMimeEssence,
BySymlinkSize,
}
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
@ -330,6 +330,8 @@ impl NodeSorterApplicable {
(NodeSorter::ByIsReadonly, true) => b.is_readonly.cmp(&a.is_readonly),
(NodeSorter::ByMimeEssence, false) => a.mime_essence.cmp(&b.mime_essence),
(NodeSorter::ByMimeEssence, true) => b.mime_essence.cmp(&a.mime_essence),
(NodeSorter::BySize, false) => a.size.cmp(&b.size),
(NodeSorter::BySize, true) => b.size.cmp(&a.size),
(NodeSorter::ByCanonicalAbsolutePath, false) => natord::compare(
&a.canonical
@ -342,23 +344,23 @@ impl NodeSorterApplicable {
.unwrap_or_default(),
),
(NodeSorter::ByICanonicalAbsolutePath, false) => natord::compare_ignore_case(
&a.canonical
(NodeSorter::ByCanonicalAbsolutePath, true) => natord::compare(
&b.canonical
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
&b.canonical
&a.canonical
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
),
(NodeSorter::ByCanonicalAbsolutePath, true) => natord::compare(
&b.canonical
(NodeSorter::ByICanonicalAbsolutePath, false) => natord::compare_ignore_case(
&a.canonical
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
&a.canonical
&b.canonical
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
@ -393,17 +395,29 @@ impl NodeSorterApplicable {
.map(|s| &s.is_dir)
.cmp(&b.canonical.as_ref().map(|s| &s.is_dir)),
(NodeSorter::ByCanonicalIsDir, true) => b
.canonical
.as_ref()
.map(|s| &s.is_dir)
.cmp(&a.canonical.as_ref().map(|s| &s.is_dir)),
(NodeSorter::ByCanonicalIsFile, false) => a
.canonical
.as_ref()
.map(|s| &s.is_file)
.cmp(&b.canonical.as_ref().map(|s| &s.is_file)),
(NodeSorter::ByCanonicalIsFile, true) => b
.canonical
.as_ref()
.map(|s| &s.is_file)
.cmp(&a.canonical.as_ref().map(|s| &s.is_file)),
(NodeSorter::ByCanonicalIsDir, true) => b
(NodeSorter::ByCanonicalIsReadonly, false) => a
.canonical
.as_ref()
.map(|s| &s.is_dir)
.cmp(&a.canonical.as_ref().map(|s| &s.is_dir)),
.map(|s| &s.is_readonly)
.cmp(&b.canonical.as_ref().map(|s| &s.is_readonly)),
(NodeSorter::ByCanonicalIsReadonly, true) => b
.canonical
@ -411,11 +425,11 @@ impl NodeSorterApplicable {
.map(|s| &s.is_readonly)
.cmp(&a.canonical.as_ref().map(|s| &s.is_readonly)),
(NodeSorter::ByCanonicalIsFile, false) => a
(NodeSorter::ByCanonicalMimeEssence, false) => a
.canonical
.as_ref()
.map(|s| &s.is_file)
.cmp(&b.canonical.as_ref().map(|s| &s.is_file)),
.map(|s| &s.mime_essence)
.cmp(&b.canonical.as_ref().map(|s| &s.mime_essence)),
(NodeSorter::ByCanonicalMimeEssence, true) => b
.canonical
@ -423,17 +437,17 @@ impl NodeSorterApplicable {
.map(|s| &s.mime_essence)
.cmp(&a.canonical.as_ref().map(|s| &s.mime_essence)),
(NodeSorter::ByCanonicalIsReadonly, false) => a
(NodeSorter::ByCanonicalSize, false) => a
.canonical
.as_ref()
.map(|s| &s.is_readonly)
.cmp(&b.canonical.as_ref().map(|s| &s.is_readonly)),
.map(|s| &s.size)
.cmp(&b.canonical.as_ref().map(|s| &s.size)),
(NodeSorter::ByCanonicalMimeEssence, false) => a
(NodeSorter::ByCanonicalSize, true) => b
.canonical
.as_ref()
.map(|s| &s.mime_essence)
.cmp(&b.canonical.as_ref().map(|s| &s.mime_essence)),
.map(|s| &s.size)
.cmp(&a.canonical.as_ref().map(|s| &s.size)),
(NodeSorter::BySymlinkAbsolutePath, false) => natord::compare(
&a.symlink
@ -446,23 +460,23 @@ impl NodeSorterApplicable {
.unwrap_or_default(),
),
(NodeSorter::ByISymlinkAbsolutePath, false) => natord::compare_ignore_case(
&a.symlink
(NodeSorter::BySymlinkAbsolutePath, true) => natord::compare(
&b.symlink
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
&b.symlink
&a.symlink
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
),
(NodeSorter::BySymlinkAbsolutePath, true) => natord::compare(
&b.symlink
(NodeSorter::ByISymlinkAbsolutePath, false) => natord::compare_ignore_case(
&a.symlink
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
&a.symlink
&b.symlink
.as_ref()
.map(|s| s.absolute_path.clone())
.unwrap_or_default(),
@ -479,17 +493,23 @@ impl NodeSorterApplicable {
.unwrap_or_default(),
),
(NodeSorter::BySymlinkExtension, false) => a
.symlink
.as_ref()
.map(|s| &s.extension)
.cmp(&b.symlink.as_ref().map(|s| &s.extension)),
(NodeSorter::BySymlinkExtension, true) => b
.symlink
.as_ref()
.map(|s| &s.extension)
.cmp(&a.symlink.as_ref().map(|s| &s.extension)),
(NodeSorter::BySymlinkExtension, false) => a
(NodeSorter::BySymlinkIsDir, false) => a
.symlink
.as_ref()
.map(|s| &s.extension)
.cmp(&b.symlink.as_ref().map(|s| &s.extension)),
.map(|s| &s.is_dir)
.cmp(&b.symlink.as_ref().map(|s| &s.is_dir)),
(NodeSorter::BySymlinkIsDir, true) => b
.symlink
@ -497,11 +517,11 @@ impl NodeSorterApplicable {
.map(|s| &s.is_dir)
.cmp(&a.symlink.as_ref().map(|s| &s.is_dir)),
(NodeSorter::BySymlinkIsDir, false) => a
(NodeSorter::BySymlinkIsFile, false) => a
.symlink
.as_ref()
.map(|s| &s.is_dir)
.cmp(&b.symlink.as_ref().map(|s| &s.is_dir)),
.map(|s| &s.is_file)
.cmp(&b.symlink.as_ref().map(|s| &s.is_file)),
(NodeSorter::BySymlinkIsFile, true) => b
.symlink
@ -509,11 +529,11 @@ impl NodeSorterApplicable {
.map(|s| &s.is_file)
.cmp(&a.symlink.as_ref().map(|s| &s.is_file)),
(NodeSorter::BySymlinkIsFile, false) => a
(NodeSorter::BySymlinkIsReadonly, false) => a
.symlink
.as_ref()
.map(|s| &s.is_file)
.cmp(&b.symlink.as_ref().map(|s| &s.is_file)),
.map(|s| &s.is_readonly)
.cmp(&b.symlink.as_ref().map(|s| &s.is_readonly)),
(NodeSorter::BySymlinkIsReadonly, true) => b
.symlink
@ -521,11 +541,11 @@ impl NodeSorterApplicable {
.map(|s| &s.is_readonly)
.cmp(&a.symlink.as_ref().map(|s| &s.is_readonly)),
(NodeSorter::BySymlinkIsReadonly, false) => a
(NodeSorter::BySymlinkMimeEssence, false) => a
.symlink
.as_ref()
.map(|s| &s.is_readonly)
.cmp(&b.symlink.as_ref().map(|s| &s.is_readonly)),
.map(|s| &s.mime_essence)
.cmp(&b.symlink.as_ref().map(|s| &s.mime_essence)),
(NodeSorter::BySymlinkMimeEssence, true) => b
.symlink
@ -533,11 +553,17 @@ impl NodeSorterApplicable {
.map(|s| &s.mime_essence)
.cmp(&a.symlink.as_ref().map(|s| &s.mime_essence)),
(NodeSorter::BySymlinkMimeEssence, false) => a
(NodeSorter::BySymlinkSize, false) => a
.symlink
.as_ref()
.map(|s| &s.mime_essence)
.cmp(&b.symlink.as_ref().map(|s| &s.mime_essence)),
.map(|s| &s.size)
.cmp(&b.symlink.as_ref().map(|s| &s.size)),
(NodeSorter::BySymlinkSize, true) => b
.symlink
.as_ref()
.map(|s| &s.size)
.cmp(&a.symlink.as_ref().map(|s| &s.size)),
}
}
}

@ -27,22 +27,6 @@ general:
table:
header:
cols:
- format: │ path
style:
fg: null
bg: null
add_modifier:
bits: 0
sub_modifier:
bits: 0
- format: type
style:
fg: null
bg: null
add_modifier:
bits: 0
sub_modifier:
bits: 0
- format: ' index'
style:
fg: null
@ -51,6 +35,9 @@ general:
bits: 0
sub_modifier:
bits: 0
- format: '╭─ path'
- format: size
- format: type
style:
fg: null
bg: null
@ -61,10 +48,10 @@ general:
height: 1
row:
cols:
- format: '{{#if isBeforeFocus}}-{{else}} {{/if}}{{{relativeIndex}}}│{{{index}}}'
- format: >
{{{tree}}}{{{prefix}}}{{{meta.icon}}}{{#if (ne meta.icon "")}} {{/if}}{{{relativePath}}}{{#if isDir}}/{{/if}}{{{suffix}}}
{{#if isSymlink}}-> {{#if isBroken}}×{{else}}{{{symlink.absolutePath}}}{{/if}}{{#if symlink.isDir}}/{{/if}}{{/if}}
style:
fg: null
bg: null
@ -72,22 +59,8 @@ general:
bits: 0
sub_modifier:
bits: 0
- format: '{{humansize size}}'
- format: '{{#if isSymlink}}{{{symlink.mimeEssence}}}{{else}}{{{mimeEssence}}}{{/if}}'
style:
fg: null
bg: null
add_modifier:
bits: 0
sub_modifier:
bits: 0
- format: '{{#if isBeforeFocus}}-{{else}} {{/if}}{{{relativeIndex}}}/{{{index}}}/{{{total}}}'
style:
fg: null
bg: null
add_modifier:
bits: 0
sub_modifier:
bits: 0
style:
fg: null
bg: null
@ -113,24 +86,11 @@ general:
sub_modifier:
bits: 0
- format: ├─
style:
fg: null
bg: null
add_modifier:
bits: 0
sub_modifier:
bits: 0
- format: ╰─
style:
fg: null
bg: null
add_modifier:
bits: 0
sub_modifier:
bits: 0
col_spacing: 3
col_widths:
- percentage: 60
- percentage: 10
- percentage: 50
- percentage: 20
- percentage: 20
default_ui:
@ -192,6 +152,8 @@ general:
format: "ro"
ByMimeEssence:
format: "mime"
BySize:
format: "size"
ByCanonicalAbsolutePath:
format: "[c]abs"
ByICanonicalAbsolutePath:
@ -206,6 +168,8 @@ general:
format: "[c]ro"
ByCanonicalMimeEssence:
format: "[c]mime"
ByCanonicalSize:
format: "[c]size"
BySymlinkAbsolutePath:
format: "[s]abs"
ByISymlinkAbsolutePath:
@ -220,6 +184,8 @@ general:
format: "[s]ro"
BySymlinkMimeEssence:
format: "[s]mime"
BySymlinkSize:
format: "[s]size"
filter_identifiers:
RelativePathIs:
@ -692,14 +658,12 @@ modes:
sorter: ByIsSymlink
reverse: true
- Explore
m:
help: by canonical mime essence
messages:
- AddNodeSorter:
sorter: ByCanonicalMimeEssence
- Explore
M:
help: by canonical mime essence reverse
messages:
@ -707,11 +671,23 @@ modes:
sorter: ByCanonicalMimeEssence
reverse: true
- Explore
s:
help: by size
messages:
- AddNodeSorter:
sorter: BySize
- Explore
S:
help: by size reverse
messages:
- AddNodeSorter:
sorter: BySize
reverse: true
- Explore
ctrl-c:
help: terminate
messages:
- Terminate
default:
messages:
- SwitchMode: default

@ -10,7 +10,8 @@ use crate::ui;
use anyhow::Result;
use crossterm::execute;
use crossterm::terminal as term;
use handlebars::Handlebars;
use handlebars::{handlebars_helper, Handlebars};
use humansize::{file_size_opts as options, FileSize};
use std::fs;
use std::io;
use std::io::prelude::*;
@ -20,6 +21,8 @@ use termion::get_tty;
use tui::backend::CrosstermBackend;
use tui::Terminal;
handlebars_helper!(to_humansize: |size: i64| size.file_size(options::CONVENTIONAL).unwrap_or_default());
fn call(app: &app::App, cmd: app::Command, silent: bool) -> io::Result<ExitStatus> {
let input_buffer = app.input_buffer().unwrap_or_default();
@ -86,6 +89,7 @@ pub fn run(mut app: app::App, focused_path: Option<String>) -> Result<Option<Str
);
let mut hb = Handlebars::new();
hb.register_helper("humansize", Box::new(to_humansize));
hb.register_template_string(
app::TEMPLATE_TABLE_ROW,
&app.config()

@ -74,6 +74,7 @@ pub struct ResolvedNodeUiMetadata {
pub is_file: bool,
pub is_readonly: bool,
pub mime_essence: String,
pub size: u64,
}
impl From<ResolvedNode> for ResolvedNodeUiMetadata {
@ -85,6 +86,7 @@ impl From<ResolvedNode> for ResolvedNodeUiMetadata {
is_file: node.is_file,
is_readonly: node.is_readonly,
mime_essence: node.mime_essence,
size: node.size,
}
}
}
@ -103,6 +105,7 @@ struct NodeUiMetadata {
pub is_file: bool,
pub is_readonly: bool,
pub mime_essence: String,
pub size: u64,
pub canonical: Option<ResolvedNodeUiMetadata>,
pub symlink: Option<ResolvedNodeUiMetadata>,
@ -146,6 +149,7 @@ impl NodeUiMetadata {
is_file: node.is_file,
is_readonly: node.is_readonly,
mime_essence: node.mime_essence.clone(),
size: node.size,
canonical: node.canonical.to_owned().map(|s| s.into()),
symlink: node.symlink.to_owned().map(|s| s.into()),
index,
@ -287,11 +291,11 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
.style(config.general.table.style.into())
.highlight_style(config.general.focus_ui.style.into())
.column_spacing(config.general.table.col_spacing.unwrap_or_default())
.block(
Block::default()
.borders(Borders::ALL)
.title(format!(" {} ", app.pwd())),
);
.block(Block::default().borders(Borders::ALL).title(format!(
" {} ({}) ",
app.pwd(),
app.directory_buffer().map(|d| d.total).unwrap_or_default()
)));
let table = table.clone().header(
Row::new(

Loading…
Cancel
Save