summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2019-08-02 15:12:11 +0200
committerCanop <cano.petrole@gmail.com>2019-08-02 15:12:11 +0200
commitf1ec96bcf72f8cff71bf1caca55f960c84ca4996 (patch)
treea163107a890751e0a43ff2db09feca1b30643363
parent25eb72fd9361f65b0b2f11e9337654637eb17f73 (diff)
whalespotting modev0.9.3
This mode is activated by the --sizes option and makes a few changes to the broot logic in order to ease size survey: - sizes computed and displayed - only one level of tree - size based ordering - hidden files and gitignored ones are shown (by default)
-rw-r--r--CHANGELOG.md4
-rw-r--r--Cargo.lock8
-rw-r--r--Cargo.toml4
-rw-r--r--README.md16
-rw-r--r--img/20190802-sizes.pngbin0 -> 38176 bytes
-rw-r--r--src/browser_states.rs4
-rw-r--r--src/cli.rs9
-rw-r--r--src/displayable_tree.rs40
-rw-r--r--src/file_sizes/file_sizes_default.rs (renamed from src/file_sizes/file_sizes_windows.rs)10
-rw-r--r--src/file_sizes/file_sizes_unix.rs11
-rw-r--r--src/file_sizes/mod.rs33
-rw-r--r--src/flat_tree.rs25
-rw-r--r--src/skin.rs9
-rw-r--r--src/tree_build.rs6
-rw-r--r--website/docs/img/20190802-sizes.pngbin0 -> 38176 bytes
-rw-r--r--website/docs/index.md13
16 files changed, 132 insertions, 60 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 850f06a..a033973 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+<a name="v0.9.3"></a>
+### v0.9.3 - 2019-08-02
+Launching broot with `--sizes` now sets a set of features enabling fast "whale spotting" navigation
+
<a name="v0.9.2"></a>
### v0.9.2 - 2019-07-31
Fix non consistent builds due to lack of precise versionning in crossterm subcrate versionning
diff --git a/Cargo.lock b/Cargo.lock
index 8ae383e..45aef5b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -86,7 +86,7 @@ dependencies = [
[[package]]
name = "broot"
-version = "0.9.1"
+version = "0.9.3"
dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -106,7 +106,7 @@ dependencies = [
"opener 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"simplelog 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "termimad 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termimad 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"umask 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -595,7 +595,7 @@ dependencies = [
[[package]]
name = "termimad"
-version = "0.6.2"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -781,7 +781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
-"checksum termimad 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1f13066780ec95e8d43f336b92d770fbef329c1b19d8ed78c7c015d2b0ba091"
+"checksum termimad 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e719aff75d732980c604d2c5a28f2d5c532f1e72cbd6eee5bae3a66547226a8"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
diff --git a/Cargo.toml b/Cargo.toml
index c2cde65..1255eec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "broot"
-version = "0.9.2"
+version = "0.9.3"
authors = ["dystroy <denys.seguret@gmail.com>"]
repository = "https://github.com/Canop/broot"
description = "Fuzzy Search + tree + cd"
@@ -30,7 +30,7 @@ crossterm_style = "=0.3.3"
crossterm_terminal = "=0.2.4"
crossterm_utils = "=0.2.3"
umask = "0.1.4"
-termimad = "0.6.2"
+termimad = "0.6.4"
#termimad = { path = "../termimad" }
[target.'cfg(unix)'.dependencies]
diff --git a/README.md b/README.md
index c9ad8cc..fd6cbba 100644
--- a/README.md
+++ b/README.md
@@ -54,18 +54,22 @@ For example, assuming you look for your one file whose name contains `abc` in a
And if you look for a filename *ending* in `abc` then you may anchor the regex: `abc$/`.
-### See what takes space:
-
-![size](img/20190128-only-folders-with-size.png)
-
-To toggle size display, type `:s`. Sizes are computed in the background, you don't have to wait for them when you navigate.
-
### Apply a personal shortcut to a file:
![size](img/20190128-edit.png)
Just find the file you want to edit with a few keystrokes, type `:e`, then `<enter>` (you should define your preferred editor, see [documentation](documentation.md#verbs)).
+### See what takes space:
+
+![size](img/20190802-sizes.png)
+
+If you start broot with the `--sizes` option, you get a mode tailored to "whale spotting" nagigation, making it easy to determine what files or folders take space.
+
+And you keep all broot tools, like filtering or the ability to delete or open files and directories.
+
+Sizes are computed in the background, you don't have to wait for them when you navigate.
+
### More...
See **[Broot's web site](https://dystroy.org/broot)** for instructions regarding installation and usage.
diff --git a/img/20190802-sizes.png b/img/20190802-sizes.png
new file mode 100644
index 0000000..f1a1c2b
--- /dev/null
+++ b/img/20190802-sizes.png
Binary files differ
diff --git a/src/browser_states.rs b/src/browser_states.rs
index 0061b13..527e611 100644
--- a/src/browser_states.rs
+++ b/src/browser_states.rs
@@ -169,9 +169,9 @@ impl BrowserState {
screen.write_status_text(
if tree.selection == 0 {
if has_pattern {
- "Hit <esc> to go back, <enter> to go up, '?' for help, or a few letters to search"
- } else {
"Hit <esc> to remove the filter, <enter> to go up, '?' for help"
+ } else {
+ "Hit <esc> to go back, <enter> to go up, '?' for help, or a few letters to search"
}
} else {
let line = &tree.lines[tree.selection];
diff --git a/src/cli.rs b/src/cli.rs
index f020a56..e25a046 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -10,7 +10,7 @@ use crossterm_style::Color;
use termimad::{Alignment, MadSkin};
use crate::errors::{ProgramError, TreeBuildError};
-use crate::tree_options::TreeOptions;
+use crate::tree_options::{OptionBool, TreeOptions};
pub struct AppLaunchArgs {
pub root: PathBuf, // what should be the initial root
@@ -132,9 +132,14 @@ pub fn read_launch_args() -> Result<AppLaunchArgs, ProgramError> {
}
let root = root.canonicalize()?;
let mut tree_options = TreeOptions::default();
+ tree_options.show_sizes = cli_args.is_present("sizes");
+ if tree_options.show_sizes {
+ // by default, if we're asked to show the size, we show all files
+ tree_options.show_hidden = true;
+ tree_options.respect_git_ignore = OptionBool::No;
+ }
tree_options.only_folders = cli_args.is_present("only-folders");
tree_options.show_hidden = cli_args.is_present("hidden");
- tree_options.show_sizes = cli_args.is_present("sizes");
tree_options.show_dates = cli_args.is_present("dates");
tree_options.show_permissions = cli_args.is_present("permissions");
if let Some(respect_ignore) = cli_args.value_of("gitignore") {
diff --git a/src/displayable_tree.rs b/src/displayable_tree.rs
index bb4982e..cc1d8fa 100644
--- a/src/displayable_tree.rs
+++ b/src/displayable_tree.rs
@@ -1,5 +1,6 @@
use std::fmt;
use std::time::SystemTime;
+use crossterm_style::{ObjectStyle};
use crossterm_terminal::{ClearType, Terminal};
use chrono::offset::Local;
use chrono::DateTime;
@@ -17,6 +18,7 @@ use crate::skin::{Skin, SkinEntry};
use crossterm_style::{Color, Colored};
use crossterm_cursor::TerminalCursor;
+use termimad::ProgressBar;
/// A tree wrapper implementing Display
/// which can be used either
@@ -50,6 +52,24 @@ impl<'s, 't> DisplayableTree<'s, 't> {
}
}
+ fn name_style(
+ &self,
+ line: &TreeLine,
+ ) -> &ObjectStyle {
+ match &line.line_type {
+ LineType::Dir => &self.skin.directory,
+ LineType::File => {
+ if line.is_exe() {
+ &self.skin.exe
+ } else {
+ &self.skin.file
+ }
+ }
+ LineType::SymLinkToFile(_) | LineType::SymLinkToDir(_) => &self.skin.link,
+ LineType::Pruning => &self.skin.pruning,
+ }
+ }
+
fn write_line_size(
&self,
f: &mut fmt::Formatter<'_>,
@@ -57,21 +77,15 @@ impl<'s, 't> DisplayableTree<'s, 't> {
total_size: Size,
) -> fmt::Result {
if let Some(s) = line.size {
- let dr: usize = s.discrete_ratio(total_size, 8) as usize;
- let s: Vec<char> = s.to_string().chars().collect();
- let mut bar = String::new();
- for i in 0..dr {
- bar.push(if i < s.len() { s[i] } else { ' ' });
- }
- self.skin.size_bar.write(f, &bar)?;
- let mut no_bar = String::new();
- for i in dr..8 {
- no_bar.push(if i < s.len() { s[i] } else { ' ' });
+ let pb = ProgressBar::new(s.part_of(total_size), 10);
+ let style = self.name_style(line);
+ if let Some(fg) = style.fg_color {
+ write!(f, "{}{:>5} {:<10} ", Colored::Fg(fg), s.to_string(), pb)
+ } else {
+ write!(f, "{:>5} {:<10} ", s.to_string(), pb)
}
- self.skin.size_no_bar.write(f, &no_bar)?;
- write!(f, " ")
} else {
- self.skin.tree.write(f, "──────── ")
+ self.skin.tree.write(f, "──────────────── ")
}
}
diff --git a/src/file_sizes/file_sizes_windows.rs b/src/file_sizes/file_sizes_default.rs
index 8b4d6c1..bed8584 100644
--- a/src/file_sizes/file_sizes_windows.rs
+++ b/src/file_sizes/file_sizes_default.rs
@@ -1,3 +1,5 @@
+//! size computation for non linux
+
use crate::task_sync::TaskLifetime;
use crossbeam::channel::unbounded;
use crossbeam::sync::WaitGroup;
@@ -64,3 +66,11 @@ pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
let size: u64 = size as u64;
Some(size)
}
+
+
+pub fn compute_file_size(path: &Path) -> u64 {
+ match fs::metadata(path) {
+ Ok(m) => m.len(),
+ Err(_) => 0,
+ }
+}
diff --git a/src/file_sizes/file_sizes_unix.rs b/src/file_sizes/file_sizes_unix.rs
index a99d81f..19b99f6 100644
--- a/src/file_sizes/file_sizes_unix.rs
+++ b/src/file_sizes/file_sizes_unix.rs
@@ -3,6 +3,7 @@ use crossbeam::channel::unbounded;
use crossbeam::sync::WaitGroup;
use std::collections::HashSet;
use std::fs;
+//use std::os::linux::fs::MetadataExt;
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::sync::{atomic::{AtomicIsize, AtomicU64, Ordering}, Arc, Mutex};
@@ -48,7 +49,8 @@ pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
continue; // let's not add the size
}
}
- size.fetch_add(md.len(), Ordering::Relaxed);
+ let file_size = if md.blocks()==0 { 0 } else { md.size() };
+ size.fetch_add(file_size, Ordering::Relaxed);
}
}
}
@@ -72,3 +74,10 @@ pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
let size = size.load(Ordering::Relaxed);
Some(size)
}
+
+pub fn compute_file_size(path: &Path) -> u64 {
+ match fs::metadata(path) {
+ Ok(m) => if m.blocks()==0 { 0 } else { m.size() },
+ Err(_) => 0,
+ }
+}
diff --git a/src/file_sizes/mod.rs b/src/file_sizes/mod.rs
index 43bd59c..be1042e 100644
--- a/src/file_sizes/mod.rs
+++ b/src/file_sizes/mod.rs
@@ -5,7 +5,6 @@
/// twice an inode.
use crate::task_sync::TaskLifetime;
use std::collections::HashMap;
-use std::fs;
use std::ops::AddAssign;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
@@ -18,10 +17,7 @@ pub struct Size(u64);
impl Size {
pub fn from_file(path: &Path) -> Size {
- Size(match fs::metadata(path) {
- Ok(m) => m.len(),
- Err(_) => 0,
- })
+ Size(compute_file_size(path))
}
/// Return the size of the directory, either by computing it of by
@@ -51,17 +47,17 @@ impl Size {
pub fn to_string(self) -> String {
let mut v = self.0;
let mut i = 0;
- while v >= 9000 && i < SIZE_NAMES.len() - 1 {
+ while v >= 5000 && i < SIZE_NAMES.len() - 1 {
v >>= 10;
i += 1;
}
format!("{}{}", v, &SIZE_NAMES[i])
}
- pub fn discrete_ratio(self, max: Size, r: u64) -> u64 {
- if max.0 == 0 || self.0 == 0 {
- 0
+ pub fn part_of(&self, total: Size) -> f32 {
+ if total.0 <= 0 {
+ 0.0
} else {
- ((r as f64) * (self.0 as f64).cbrt() / (max.0 as f64).cbrt()).round() as u64
+ self.0 as f32 / total.0 as f32
}
}
}
@@ -94,10 +90,19 @@ fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
file_sizes_unix::compute_dir_size(path, tl)
}
-#[cfg(windows)]
-mod file_sizes_windows;
+#[cfg(unix)]
+fn compute_file_size(path: &Path) -> u64 {
+ file_sizes_unix::compute_file_size(path)
+}
-#[cfg(windows)]
+#[cfg(not(unix))]
+mod file_sizes_default;
+
+#[cfg(not(unix))]
fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
- file_sizes_windows::compute_dir_size(path, tl)
+ file_sizes_default::compute_dir_size(path, tl)
+}
+#[cfg(not(unix))]
+fn compute_file_size(path: &Path) -> u64 {
+ file_sizes_default::compute_file_size(path)
}
diff --git a/src/flat_tree.rs b/src/flat_tree.rs
index 61c6b76..61eb68b 100644
--- a/src/flat_tree.rs
+++ b/src/flat_tree.rs
@@ -1,6 +1,6 @@
/// In the flat_tree structure, every "node" is just a line, there's
/// no link from a child to its parent or from a parent to its children.
-use std::cmp::{self, Ordering};
+use std::cmp::{self, Ord, PartialOrd, Ordering};
use std::fs;
use std::mem;
use std::path::{Path, PathBuf};
@@ -157,11 +157,8 @@ impl PartialOrd for TreeLine {
impl Tree {
pub fn refresh(&mut self, page_height: usize) -> Result<(), errors::TreeBuildError> {
- let builder =
- TreeBuilder::from(self.root().to_path_buf(), self.options.clone(), page_height)?;
- debug!("remove 3");
+ let builder = TreeBuilder::from(self.root().to_path_buf(), self.options.clone(), page_height)?;
let mut tree = builder.build(&TaskLifetime::unlimited()).unwrap(); // should not fail
- debug!("remove 4");
// we save the old selection to try restore it
let selected_path = self.selected_line().path.to_path_buf();
mem::swap(&mut self.lines, &mut tree.lines);
@@ -174,8 +171,8 @@ impl Tree {
// - sort the lines
// - compute left branchs
pub fn after_lines_changed(&mut self) {
- // we sort the lines
- self.lines.sort();
+ // we sort the lines (this is mandatory to avoid crashes)
+ self.lines[1..].sort();
for i in 1..self.lines.len() {
for d in 0..self.lines[i].left_branchs.len() {
@@ -372,15 +369,29 @@ impl Tree {
self.lines[i].size = Some(Size::from_file(&self.lines[i].path));
}
}
+ self.sort_siblings_by_size();
}
pub fn fetch_some_missing_dir_size(&mut self, tl: &TaskLifetime) {
for i in 1..self.lines.len() {
if self.lines[i].size.is_none() && self.lines[i].line_type == LineType::Dir {
self.lines[i].size = Size::from_dir(&self.lines[i].path, tl);
+ self.sort_siblings_by_size();
return;
}
}
}
+ /// Sort files according to their size
+ ///
+ /// Warning: must not be called if there's more than one level displayed!
+ /// (a better sort should be devised but it's unsure whether it would be
+ /// readable enough)
+ fn sort_siblings_by_size(&mut self) {
+ self.lines[1..].sort_by(|a, b| {
+ let asize = a.size.map_or(0, |s| s.into());
+ let bsize = b.size.map_or(0, |s| s.into());
+ bsize.cmp(&asize)
+ });
+ }
pub fn total_size(&self) -> Size {
if let Some(size) = self.lines[0].size {
// if the real total size is computed, it's in the root line
diff --git a/src/skin.rs b/src/skin.rs
index 9d92c24..fd285eb 100644
--- a/src/skin.rs
+++ b/src/skin.rs
@@ -94,19 +94,18 @@ Skin! {
pruning: gray(17), None; {Italic}
permissions: gray(15), None;
dates: ansi(109), None;
- selected_line: None, gray(3);
- size_bar: Some(White), Some(DarkBlue);
- size_no_bar: gray(15), gray(2);
+ selected_line: None, gray(4);
+ size_bar: Some(White), gray(2);
char_match: Some(Green), None;
file_error: Some(Red), None;
flag_label: gray(15), gray(1);
- flag_value: Some(Blue), gray(1);
+ flag_value: Some(Blue), gray(1); {Bold}
input: Some(White), None;
spinner: gray(10), gray(2);
status_error: Some(White), Some(Red);
status_normal: Some(White), gray(2);
scrollbar_track: gray(7), None;
- scrollbar_thumb: ansi(178), None;
+ scrollbar_thumb: gray(22), None;
help_paragraph: gray(20), None;
help_bold: ansi(178), None; {Bold}
help_italic: ansi(229), None; {Italic}
diff --git a/src/tree_build.rs b/src/tree_build.rs
index c9c47f5..9761bf6 100644
--- a/src/tree_build.rs
+++ b/src/tree_build.rs
@@ -369,6 +369,12 @@ impl TreeBuilder {
}
} else {
// this depth is finished, we must go deeper
+ if self.options.show_sizes {
+ // both for technical reasons (bad sort) and ergonomics
+ // ones (it proved to be hard to read), we don't want
+ // a deep tree when looking at sizes.
+ break;
+ }
if next_level_dirs.is_empty() {
// except there's nothing deeper
break;
diff --git a/website/docs/img/20190802-sizes.png b/website/docs/img/20190802-sizes.png
new file mode 100644
index 0000000..f1a1c2b
--- /dev/null
+++ b/website/docs/img/20190802-sizes.png
Binary files differ
diff --git a/website/docs/index.md b/website/docs/index.md
index 1ffac21..66a8d32 100644
--- a/website/docs/index.md
+++ b/website/docs/index.md
@@ -44,7 +44,7 @@ Once the file you want is selected you can
# Manipulate your files
-![size](img/20190306-mv.png)
+![mv](img/20190306-mv.png)
Most often you move your files in the blind. You do a few `ls` before, then your manipulation, and maybe you check after.
@@ -58,11 +58,16 @@ Move, copy, rm, mkdir, are built in and you can add your own shortcuts.
Just find the file you want to edit with a few keystrokes, type `:e`, then `<enter>` (you should define your preferred editor, see [documentation](documentation/usage.md#verbs)).
-# See what takes space
+### See what takes space:
-![size](img/20190128-only-folders-with-size.png)
+![size](img/20190802-sizes.png)
+
+If you start broot with the `--sizes` option, you get a mode tailored to "whale spotting" nagigation, making it easy to determine what files or folders take space.
+
+And you keep all broot tools, like filtering or the ability to delete or open files and directories.
+
+Sizes are computed in the background, you don't have to wait for them when you navigate.
-To toggle size display, type `:s`. Sizes are computed in the background, you don't have to wait for them when you navigate.
# More...