Skip to content

Commit

Permalink
whalespotting mode
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
Canop committed Aug 2, 2019
1 parent 25eb72f commit f1ec96b
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 60 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "broot"
version = "0.9.2"
version = "0.9.3"
authors = ["dystroy <[email protected]>"]
repository = "https://github.com/Canop/broot"
description = "Fuzzy Search + tree + cd"
Expand Down Expand Up @@ -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]
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Binary file added img/20190802-sizes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/browser_states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
9 changes: 7 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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") {
Expand Down
40 changes: 27 additions & 13 deletions src/displayable_tree.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -50,28 +52,40 @@ 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<'_>,
line: &TreeLine,
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, "──────────────── ")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! size computation for non linux
use crate::task_sync::TaskLifetime;
use crossbeam::channel::unbounded;
use crossbeam::sync::WaitGroup;
Expand Down Expand Up @@ -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,
}
}
11 changes: 10 additions & 1 deletion src/file_sizes/file_sizes_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
}
}
}
Expand All @@ -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,
}
}
33 changes: 19 additions & 14 deletions src/file_sizes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
}
}
}
Expand Down Expand Up @@ -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)
}
25 changes: 18 additions & 7 deletions src/flat_tree.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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);
Expand All @@ -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() {
Expand Down Expand Up @@ -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
Expand Down
9 changes: 4 additions & 5 deletions src/skin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Loading

0 comments on commit f1ec96b

Please sign in to comment.