summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2020-06-28 15:33:43 +0200
committerCanop <cano.petrole@gmail.com>2020-06-28 15:33:43 +0200
commit3accec2a8ea0827b84b7014744cfe94834cc18d5 (patch)
tree33455c638391102566c2c3db8d4bed876b7cd2a7
parent6ae4cc421ef3db9f6f458a023780af899313f821 (diff)
new `cols_order` attribute in configurationv0.18.1
Allows setting the order of columns, most notably the position of the branch (left of the tree, just before the name, or in between). Fix #127
-rw-r--r--CHANGELOG.md4
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.md31
-rw-r--r--src/app/context.rs22
-rw-r--r--src/browser/browser_state.rs3
-rw-r--r--src/cli.rs4
-rw-r--r--src/conf/conf.rs6
-rw-r--r--src/conf/default_conf.rs30
-rw-r--r--src/display/col.rs70
-rw-r--r--src/display/displayable_tree.rs318
-rw-r--r--src/display/mod.rs2
-rw-r--r--src/errors.rs3
-rw-r--r--src/launchable.rs9
-rw-r--r--src/print.rs8
-rw-r--r--src/skin/app_skin.rs7
-rw-r--r--src/skin/style_map.rs2
-rw-r--r--src/tree/tree_options.rs4
-rw-r--r--website/docs/conf_file.md300
-rw-r--r--website/docs/conf_verbs.md297
-rw-r--r--website/docs/img/20200628-sdp.pngbin0 -> 158128 bytes
-rw-r--r--website/docs/img/20200628-whale-spotting.pngbin0 -> 48102 bytes
-rw-r--r--website/docs/index.md6
-rw-r--r--website/mkdocs.yml7
24 files changed, 657 insertions, 480 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c800fe7..42d3bfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+<a name="v0.18.1"></a>
+### v0.18.1 - 2020-06-28
+- column order configurable - Fix #127
+
<a name="v0.18.0"></a>
### v0.18.0 - 2020-06-26
#### Major change: Recursive last modified date computation
diff --git a/Cargo.lock b/Cargo.lock
index c2ae1cd..33f9e1f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -84,7 +84,7 @@ dependencies = [
[[package]]
name = "broot"
-version = "0.18.0"
+version = "0.18.1"
dependencies = [
"bet",
"chrono",
diff --git a/Cargo.toml b/Cargo.toml
index 6aee50d..1aadd14 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "broot"
-version = "0.18.0"
+version = "0.18.1"
authors = ["dystroy <denys.seguret@gmail.com>"]
repository = "https://github.com/Canop/broot"
documentation = "https://dystroy.org/broot"
diff --git a/README.md b/README.md
index 7239fc4..f338792 100644
--- a/README.md
+++ b/README.md
@@ -33,14 +33,9 @@ Table of Contents
* [Check git statuses](#check-git-statuses)
* [Further Reading](#further-reading)
-## Installation
-Full installation documentation can be found [here](https://dystroy.org/broot/documentation/installation/).
-
-## Feature Showcase
-
### Get an overview of a directory, even a big one
-![overview](img/20191112-overview.png)
+![overview](website/docs/img/20191112-overview.png)
Notice the *unlisted*?
@@ -50,7 +45,7 @@ That's what makes it usable where the old `tree` command would produce pages of
### Find a directory then `cd` to it
-![cd](img/20191112-cd.png)
+![cd](website/docs/img/20191112-cd.png)
This way, you can navigate to a directory with the minimum amount of keystrokes, even if you don't exactly remember where it is.
@@ -66,7 +61,7 @@ Most useful keys for this:
### Never lose track of file hierarchy while you search
-![size](img/20191112-mycnf.png)
+![size](website/docs/img/20191112-mycnf.png)
broot tries to select the most relevant file. You can still go from one match to another one using <kbd>tab</kbd> or arrow keys.
@@ -76,7 +71,7 @@ And you have other types of searches, for example searching on file content (sta
![content search](website/docs/img/20200620-content-memm.png)
-You may also apply logical operators or combine patterns, for example searching `test` in all files except txt ones could be `!/txt$/&c/test` and searching `carg` both in file names and file contents would be `carg|c/carg`.
+You may also apply logical operators or combine patterns, for example searching `test` in all files except json ones could be `!/json$/&c/test` and searching `carg` both in file names and file contents would be `carg|c/carg`.
Once the file you want is selected you can
@@ -86,7 +81,7 @@ Once the file you want is selected you can
### Manipulate your files
-![mv](img/20191112-mv.png)
+![mv](website/docs/img/20191112-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.
@@ -110,31 +105,31 @@ If you like you may do it Norton Commander style by binding `:copy_to_panel` to
### Apply a standard or personal shortcut to a file
-![size](img/20191112-edit.png)
+![size](website/docs/img/20191112-edit.png)
Just find the file you want to edit with a few keystrokes, type `:e`, then <kbd>enter</kbd>.
-You can add verbs or configure the existing ones; see [documentation](documentation/usage.md#verbs).
+You can add verbs or configure the existing ones; see [documentation](../conf_file/#verbs-shortcuts-and-keys).
And you can add shorcuts, for example a <kbd>ctrl</kbd> sequence or a function key
### Replace `ls` (and its clones):
-If you want to display *dates* and *permissions*, do `br -dp` which gets you this:
+If you want to display *sizes*, *dates* and *permissions*, do `br -sdp` which gets you this:
-![replace ls](img/20191214-replace-ls.png)
+![replace ls](website/docs/img/20200628-sdp.png)
You may also toggle options with a few keystrokes while inside broot. For example hitting a space, a `d` then enter shows you the dates. Or a space, then `h` then enter and you see hidden files.
### Sort, see what takes space:
-You can toggle sorts from inside broot, or start broot with `--sort-by-size` or `--sort-by-date`.
+You may sort by launching broot with `--sort-by-size` or `--sort-by-date`. Or you may, inside broot, just type a space, then `sd`, and <kbd>enter</kbd> and you toggled the `:sort_by_date` mode.
When sorting, the whole content of directories is taken into account. So if you want to find on monday morning the most recently modified files, just launch `br --sort-by-date ~`.
If you start broot with the `--whale-spotting` option (or its shorcut `-w`), you get a mode tailored to "whale spotting" navigation, making it easy to determine what files or folders take space.
-![size](img/20191112-sizes.png)
+![size](website/docs/img/20200628-whale-spotting.png)
And you keep all broot tools, like filtering or the ability to delete or open files and directories.
@@ -142,11 +137,11 @@ Sizes, dates, files counts, are computed in the background, you don't have to wa
### check git statuses:
-![size](img/20200203-git.png)
+![size](website/docs/img/20200203-git.png)
Use `:gf` to display the statuses of files (what are the new ones, the modified ones, etc.), the current branch name and the change statistics.
-And if you want to see *only* the files which would be displayed by the `git status` command, do `:gs`.
+And if you want to see *only* the files which would be displayed by the `git status` command, do `:gs`. From there it's easy to edit, or diff, selected files.
## Further Reading
See **[Broot's web site](https://dystroy.org/broot)** for instructions regarding installation and usage.
diff --git a/src/app/context.rs b/src/app/context.rs
index ffd9e14..d5fe915 100644
--- a/src/app/context.rs
+++ b/src/app/context.rs
@@ -1,6 +1,7 @@
use crate::{
cli::AppLaunchArgs,
conf::Conf,
+ display::{Cols, DEFAULT_COLS},
pattern::SearchModeMap,
tree::SpecialPath,
verb::VerbStore,
@@ -9,27 +10,40 @@ use crate::{
/// The immutable container that can be passed around
/// to provide the configuration things
pub struct AppContext {
+
+ /// where's the config file we're using
pub config_path: String,
+
+ /// all the arguments specified at launch
pub launch_args: AppLaunchArgs,
+
+ /// the verbs in use (builtins and configured ones)
pub verb_store: VerbStore,
+
+ /// the paths for which there's a special behavior to follow (come from conf)
pub special_paths: Vec<SpecialPath>,
+
+ /// the map between search prefixes and the search mode to apply
pub search_modes: SearchModeMap,
+
+ /// order of columns in tree display
+ pub cols: Cols,
}
impl AppContext {
pub fn from(
launch_args: AppLaunchArgs,
verb_store: VerbStore,
- special_paths: Vec<SpecialPath>,
- search_modes: SearchModeMap,
+ config: &Conf,
) -> Self {
let config_path = Conf::default_location().to_string_lossy().to_string();
Self {
config_path,
launch_args,
verb_store,
- special_paths,
- search_modes,
+ special_paths: config.special_paths.clone(),
+ search_modes: config.search_modes.clone(),
+ cols: config.cols_order.unwrap_or(DEFAULT_COLS).clone(),
}
}
}
diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs
index 0da7406..dab6c85 100644
--- a/src/browser/browser_state.rs
+++ b/src/browser/browser_state.rs
@@ -698,11 +698,12 @@ impl AppState for BrowserState {
_screen: &Screen,
area: Area,
panel_skin: &PanelSkin,
- _con: &AppContext,
+ con: &AppContext,
) -> Result<(), ProgramError> {
let dp = DisplayableTree {
tree: &self.displayed_tree(),
skin: &panel_skin.styles,
+ cols: &con.cols,
area,
in_app: true,
};
diff --git a/src/cli.rs b/src/cli.rs
index 42a46c5..4dab01d 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -192,9 +192,7 @@ pub fn run() -> Result<Option<Launchable>, ProgramError> {
no_style,
};
- let context = AppContext::from(
- launch_args, verb_store, config.special_paths.clone(), config.search_modes.clone(),
- );
+ let context = AppContext::from(launch_args, verb_store, &config);
let mut w = display::writer();
let mut screen = Screen::new(&context, &config)?;
let app = App::new(&context, &screen)?;
diff --git a/src/conf/conf.rs b/src/conf/conf.rs
index 573963e..a17ce6a 100644
--- a/src/conf/conf.rs
+++ b/src/conf/conf.rs
@@ -6,6 +6,7 @@ use {
default_conf::DEFAULT_CONF_FILE,
},
crate::{
+ display::{Col, Cols},
errors::ConfError,
keys,
pattern::{SearchModeMap, SearchModeMapEntry},
@@ -34,6 +35,7 @@ pub struct Conf {
pub special_paths: Vec<SpecialPath>,
pub search_modes: SearchModeMap,
pub disable_mouse_capture: bool,
+ pub cols_order: Option<Cols>,
}
fn string_field(value: &Value, field_name: &str) -> Option<String> {
@@ -116,6 +118,10 @@ impl Conf {
if let Some(mouse_capture) = bool_field(&root, "capture_mouse") {
self.disable_mouse_capture = !mouse_capture;
}
+ // cols order
+ self.cols_order = string_field(&root, "cols_order")
+ .map(|s| Col::parse_cols(&s))
+ .transpose()?;
// reading verbs
if let Some(Value::Array(verbs_value)) = &root.get("verbs") {
for verb_value in verbs_value.iter() {
diff --git a/src/conf/default_conf.rs b/src/conf/default_conf.rs
index e24eff5..21d6c83 100644
--- a/src/conf/default_conf.rs
+++ b/src/conf/default_conf.rs
@@ -28,14 +28,6 @@ pub const DEFAULT_CONF_FILE: &str = r#"
default_flags = ""
###############################################################
-# Date/Time format
-# If you want to change the format for date/time, uncomment the
-# following line and change it according to
-# https://docs.rs/chrono/0.4.11/chrono/format/strftime/index.html
-#
-# date_time_format = "%Y/%m/%d %R "
-
-###############################################################
# Special paths
# If some paths must be handled specially, uncomment (and change
# this section as per the examples
@@ -46,6 +38,28 @@ default_flags = ""
# "/home/dys/my-link-I-want-to-explore" = "enter"
###############################################################
+# Date/Time format
+# If you want to change the format for date/time, uncomment the
+# following line and change it according to
+# https://docs.rs/chrono/0.4.11/chrono/format/strftime/index.html
+#
+# date_time_format = "%Y/%m/%d %R"
+
+###############################################################
+# Column order
+# cols_order, if specified, must be a permutation of "gbpdscn"
+# where every char denotes a column:
+# g : Git file info
+# b : branch (shows the depth and parent in the tree)
+# p : permissions (mode, user, group)
+# d : last modification date
+# s : size (with size bar when sorting)
+# c : count, number of files in directories
+# n : file name
+#
+# cols_order = "gbdscn"
+
+###############################################################
# Verbs and shortcuts
# You can define your own commands which would be applied to
# the selection.
diff --git a/src/display/col.rs b/src/display/col.rs
new file mode 100644
index 0000000..df4957d
--- /dev/null
+++ b/src/display/col.rs
@@ -0,0 +1,70 @@
+use {
+ crate::{
+ errors::ConfError,
+ },
+};
+
+// number of columns in enum
+const COLS_COUNT: usize = 7;
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum Col {
+ Git,
+ Branch,
+ Permission,
+ Date,
+ Size, // includes the size bar in sort mode
+ Count,
+ Name, // name or subpath, depending on sort
+}
+impl Col {
+ pub fn parse(c: char) -> Result<Self, ConfError> {
+ Ok(match c {
+ 'g' => Self::Git,
+ 'b' => Self::Branch,
+ 'd' => Self::Date,
+ 's' => Self::Size,
+ 'c' => Self::Count,
+ 'n' => Self::Name,
+ _ => Err(ConfError::InvalidCols { details: format!("column not recognized : {}", c) })?,
+ })
+ }
+ pub fn index_in(self, cols: &Cols) -> Option<usize> {
+ for (idx, col) in cols.iter().enumerate() {
+ if *col==self {
+ return Some(idx);
+ }
+ }
+ None
+ }
+ /// return a Cols which tries to take the s setting into account
+ /// but is guaranteed to have every Col exactly once.
+ pub fn parse_cols(s: &str) -> Result<Cols, ConfError> {
+ let mut cols = DEFAULT_COLS.clone();
+ for (idx, c) in s.chars().enumerate() {
+ if idx >= COLS_COUNT {
+ return Err(ConfError::InvalidCols { details: format!("too long: {:?}", s) });
+ }
+ // we swap the cols, to ensure both keeps being present
+ let col = Col::parse(c)?;
+ let dest_idx = col.index_in(&cols).unwrap(); // can't be none by construct
+ cols[dest_idx] = cols[idx];
+ cols[idx] = col;
+ }
+ Ok(cols)
+ }
+}
+
+pub type Cols = [Col;COLS_COUNT];
+
+/// Default column order
+pub static DEFAULT_COLS: Cols = [
+ Col::Git,
+ Col::Size,
+ Col::Count,
+ Col::Permission,
+ Col::Date,
+ Col::Branch,
+ Col::Name,
+];
+
diff --git a/src/display/displayable_tree.rs b/src/display/displayable_tree.rs
index 99ae99d..8b71183 100644
--- a/src/display/displayable_tree.rs
+++ b/src/display/displayable_tree.rs
@@ -1,5 +1,7 @@
use {
super::{
+ Col,
+ Cols,
CropWriter,
GitStatusDisplay,
MatchedString,
@@ -40,14 +42,21 @@ pub struct DisplayableTree<'s, 't> {
pub skin: &'s StyleMap,
pub area: termimad::Area,
pub in_app: bool, // if true we show the selection and scrollbar
+ pub cols: &'s Cols,
}
impl<'s, 't> DisplayableTree<'s, 't> {
- pub fn out_of_app(tree: &'t Tree, skin: &'s StyleMap, width: u16) -> DisplayableTree<'s, 't> {
+ pub fn out_of_app(
+ tree: &'t Tree,
+ skin: &'s StyleMap,
+ cols: &'s Cols,
+ width: u16,
+ ) -> DisplayableTree<'s, 't> {
DisplayableTree {
tree,
skin,
+ cols,
area: termimad::Area {
left: 0,
top: 0,
@@ -73,113 +82,102 @@ impl<'s, 't> DisplayableTree<'s, 't> {
}
}
- fn write_line_count<'w, W>(
+ fn write_line_count<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
line: &TreeLine,
selected: bool,
- ) -> Result<(), termimad::Error>
- where
- W: Write,
- {
- if let Some(s) = line.sum {
+ ) -> Result<usize, termimad::Error> {
+ Ok(if let Some(s) = line.sum {
cond_bg!(count_style, self, selected, self.skin.count);
cw.queue_string(&count_style, format!("{:>8}", s.to_count()))?;
- cw.queue_char(&self.skin.default, ' ')
+ 1
} else {
- cw.queue_str(&self.skin.tree, "─────────")
- }
+ 9
+ })
}
- fn write_line_size<'w, W>(
+ fn write_line_size<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
line: &TreeLine,
selected: bool,
- ) -> Result<(), termimad::Error>
- where
- W: Write,
- {
- if let Some(s) = line.sum {
+ ) -> Result<usize, termimad::Error> {
+ Ok(if let Some(s) = line.sum {
cond_bg!(size_style, self, selected, self.name_style(&line));
- cw.queue_string(&size_style, s.to_size_string())?;
- cw.queue_char(&self.skin.default, ' ')
+ cw.queue_string(&size_style, format!("{:>5}", s.to_size_string()))?;
+ 1
} else {
- cw.queue_str(&self.skin.tree, "─────")
- }
+ 6
+ })
}
/// only makes sense when there's only one level
/// (so in sort mode)
- fn write_line_size_with_bar<'w, W>(
+ fn write_line_size_with_bar<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
line: &TreeLine,
total_size: FileSum,
selected: bool,
- ) -> Result<(), termimad::Error>
- where
- W: Write,
- {
- if let Some(s) = line.sum {
+ ) -> Result<usize, termimad::Error> {
+ Ok(if let Some(s) = line.sum {
let pb = ProgressBar::new(s.part_of_size(total_size), 10);
cond_bg!(size_style, self, selected, self.name_style(&line));
cond_bg!(sparse_style, self, selected, self.skin.sparse);
cw.queue_string(&size_style, format!("{:>5}", s.to_size_string()))?;
cw.queue_char(&sparse_style, if s.is_sparse() { 's' } else { ' ' })?;
- cw.queue_string(&size_style, format!("{:<10} ", pb))
+ cw.queue_string(&size_style, format!("{:<10}", pb))?;
+ 1
} else {
- cw.queue_str(&self.skin.tree, "─────────────────")
- }
+ 17
+ })
}
- fn write_line_git_status<'w, W>(
+ fn write_line_git_status<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
line: &TreeLine,
- ) -> Result<(), termimad::Error>
- where
- W: Write,
- {
+ selected: bool,
+ ) -> Result<usize, termimad::Error> {
+ let (style, char) =
if !line.is_selectable() {
- cw.queue_char(&self.skin.tree, ' ')
+ (&self.skin.tree, ' ')
} else {
match line.git_status.map(|s| s.status) {
- Some(Status::CURRENT) => cw.queue_char(&self.skin.git_status_current, ' '),
- Some(Status::WT_NEW) => cw.queue_char(&self.skin.git_status_new, 'N'),
- Some(Status::CONFLICTED) => cw.queue_char(&self.skin.git_status_conflicted, 'C'),
- Some(Status::WT_MODIFIED) => cw.queue_char(&self.skin.git_status_modified, 'M'),
- Some(Status::IGNORED) => cw.queue_char(&self.skin.git_status_ignored, 'I'),
- None => cw.queue_char(&self.skin.tree, ' '),
- _ => cw.queue_char(&self.skin.git_status_other, '?'),
+ Some(Status::CURRENT) => (&self.skin.git_status_current, ' '),
+ Some(Status::WT_NEW) => (&self.skin.git_status_new, 'N'),
+ Some(Status::CONFLICTED) => (&self.skin.git_status_conflicted, 'C'),
+ Some(Status::WT_MODIFIED) => (&self.skin.git_status_modified, 'M'),
+ Some(Status::IGNORED) => (&self.skin.git_status_ignored, 'I'),
+ None => (&self.skin.tree, ' '),
+ _ => (&self.skin.git_status_other, '?'),
}
- }
+ };
+ cond_bg!(git_style, self, selected, style);
+ cw.queue_char(git_style, char)?;
+ Ok(0)
}
- fn write_date<'w, W>(
+ fn write_date<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
seconds: i64,
selected: bool,
- ) -> Result<(), termimad::Error>
- where
- W: Write,
- {
+ ) -> Result<usize, termimad::Error> {
let date_time: DateTime<Local> = Local.timestamp(seconds, 0);
cond_bg!(date_style, self, selected, self.skin.dates);
- cw.queue_string(date_style, date_time.format(self.tree.options.date_time_format).to_string())
+ cw.queue_string(date_style, date_time.format(self.tree.options.date_time_format).to_string())?;
+ Ok(1)
}
#[cfg(unix)]
- fn write_mode<'w, W>(
+ fn write_mode<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
mode: Mode,
selected: bool,
- ) -> Result<(), termimad::Error>
- where
- W: Write,
- {
+ ) -> Result<(), termimad::Error> {
cond_bg!(n_style, self, selected, self.skin.perm__);
cond_bg!(r_style, self, selected, self.skin.perm_r);
cond_bg!(w_style, self, selected, self.skin.perm_w);
@@ -236,18 +234,71 @@ impl<'s, 't> DisplayableTree<'s, 't> {
Ok(())
}
+ #[cfg(unix)]
+ fn write_permissions<'w, W: Write>(
+ &self,
+ cw: &mut CropWriter<'w, W>,
+ line: &TreeLine,
+ user_group_max_lengths: (usize, usize),
+ selected: bool,
+ ) -> Result<usize, ProgramError> {
+ Ok(if line.is_selectable() {
+ self.write_mode(cw, line.mode(), selected)?;
+ let owner = permissions::user_name(line.metadata.uid());
+ cond_bg!(owner_style, self, selected, self.skin.owner);
+ cw.queue_string(
+ &owner_style,
+ format!(" {:w$}", &owner, w = user_group_max_lengths.0),
+ )?;
+ let group = permissions::group_name(line.metadata.gid());
+ cond_bg!(group_style, self, selected, self.skin.group);
+ cw.queue_string(
+ &group_style,
+ format!(" {:w$}", &group, w = user_group_max_lengths.1),
+ )?;
+ 1
+ } else {
+ 9 + 1 + user_group_max_lengths.0 + 1 + user_group_max_lengths.1 + 1
+ })
+ }
+
+ fn write_branch<'w, W: Write>(
+ &self,
+ cw: &mut CropWriter<'w, W>,
+ line_index: usize,
+ line: &TreeLine,
+ selected: bool,
+ ) -> Result<usize, ProgramError> {
+ cond_bg!(branch_style, self, selected, self.skin.tree);
+ for depth in 0..line.depth {
+ cw.queue_str(
+ &branch_style,
+ if line.left_branchs[depth as usize] {
+ if self.tree.has_branch(line_index + 1, depth as usize) {
+ if depth == line.depth - 1 {
+ "├──"
+ } else {
+ "│ "
+ }
+ } else {
+ "└──"
+ }
+ } else {
+ " "
+ },
+ )?;
+ }
+ Ok(0)
+ }
/// write the name or subpath, depending on the pattern_object
- fn write_line_label<'w, W>(
+ fn write_line_label<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
line: &TreeLine,
pattern_object: PatternObject,
selected: bool,
- ) -> Result<(), ProgramError>
- where
- W: Write,
- {
+ ) -> Result<usize, ProgramError> {
let style = match &line.line_type {
TreeLineType::Dir => &self.skin.directory,
TreeLineType::File => {
@@ -297,18 +348,15 @@ impl<'s, 't> DisplayableTree<'s, 't> {
}
_ => {}
}
- Ok(())
+ Ok(1)
}
- fn write_content_extract<'w, W>(
+ fn write_content_extract<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
extract: ContentMatch,
selected: bool,
- ) -> Result<(), ProgramError>
- where
- W: Write,
- {
+ ) -> Result<(), ProgramError> {
cond_bg!(extract_style, self, selected, self.skin.content_extract);
cond_bg!(match_style, self, selected, self.skin.content_match);
cw.queue_str(&extract_style, " ")?;
@@ -322,14 +370,11 @@ impl<'s, 't> DisplayableTree<'s, 't> {
Ok(())
}
- pub fn write_root_line<'w, W>(
+ pub fn write_root_line<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
selected: bool,
- ) -> Result<(), ProgramError>
- where
- W: Write,
- {
+ ) -> Result<(), ProgramError> {
cond_bg!(style, self, selected, self.skin.directory);
let title = self.tree.lines[0].path.to_string_lossy();
cw.queue_str(&style, &title)?;
@@ -351,14 +396,11 @@ impl<'s, 't> DisplayableTree<'s, 't> {
}
/// if in app, extend the background till the end of screen row
- pub fn extend_line_bg<'w, W>(
+ pub fn extend_line_bg<'w, W: Write>(
&self,
cw: &mut CropWriter<'w, W>,
selected: bool,
- ) -> Result<(), ProgramError>
- where
- W: Write,
- {
+ ) -> Result<(), ProgramError> {
if self.in_app {
if selected {
cw.queue_bg(&self.skin.selected_line)?;
@@ -371,10 +413,7 @@ impl<'s, 't> DisplayableTree<'s, 't> {
}
/// write the whole tree on the given `W`
- pub fn write_on<W>(&self, f: &mut W) -> Result<(), ProgramError>
- where
- W: Write,
- {
+ pub fn write_on<W: Write>(&self, f: &mut W) -> Result<(), ProgramError> {
let tree = self.tree;
#[cfg(unix)]
let user_group_max_lengths = user_group_max_lengths(&tree);
@@ -392,6 +431,15 @@ impl<'s, 't> DisplayableTree<'s, 't> {
let pattern_object = tree.options.pattern.pattern.object();
self.write_root_line(&mut cw, tree.selection == 0)?;
f.queue(SetBackgroundColor(Color::Reset))?;
+
+ // we compute the length of the dates, depending on the format
+ let date_len = if tree.options.show_dates {
+ let date_time: DateTime<Local> = Local::now();
+ date_time.format(tree.options.date_time_format).to_string().len()
+ } else {
+ 0 // we don't care
+ };
+
for y in 1..self.area.height {
if self.in_app {
f.queue(cursor::MoveTo(self.area.left, y + self.area.top))?;
@@ -408,72 +456,66 @@ impl<'s, 't> DisplayableTree<'s, 't> {
if line_index < tree.lines.len() {
let line = &tree.lines[line_index];
selected = self.in_app && line_index == tree.selection;
- if !tree.git_status.is_none() {
- self.write_line_git_status(cw, line)?;
- }
- for depth in 0..line.depth {
- cw.queue_str(
- &self.skin.tree,
- if line.left_branchs[depth as usize] {
- if self.tree.has_branch(line_index + 1, depth as usize) {
- if depth == line.depth - 1 {
- "├──"
- } else {
- "│ "
- }
+ let mut in_branch = false;
+ for col in self.cols {
+ let void_len = match col {
+
+ Col::Git if !tree.git_status.is_none() => {
+ self.write_line_git_status(cw, line, selected)?
+ }
+
+ Col::Branch => {
+ in_branch = true;
+ self.write_branch(cw, line_index, line, selected)?
+ }
+
+ #[cfg(unix)]
+ Col::Permission if tree.options.show_permissions => {
+ self.write_permissions(cw, line, user_group_max_lengths, selected)?
+ }
+
+ Col::Date if tree.options.show_dates => {
+ if let Some(seconds) = line.sum.and_then(|sum| sum.to_valid_seconds()) {
+ self.write_date(cw, seconds, selected)?
} else {
- "└──"
+ date_len + 1
}
- } else {
- " "
- },
- )?;
- }
- #[cfg(unix)]
- {
- if tree.options.show_permissions {
- if line.is_selectable() {
- self.write_mode(cw, line.mode(), selected)?;
- let owner = permissions::user_name(line.metadata.uid());
- cond_bg!(owner_style, self, selected, self.skin.owner);
- cw.queue_string(
- &owner_style,
- format!(" {:w$}", &owner, w = user_group_max_lengths.0,),
- )?;
- let group = permissions::group_name(line.metadata.gid());
- cond_bg!(group_style, self, selected, self.skin.group);
- cw.queue_string(
- &grou