summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2020-10-07 18:38:07 +0200
committerCanop <cano.petrole@gmail.com>2020-10-07 18:38:07 +0200
commit4b6a90a86d62902bb2795b5b721635fdaa87faa5 (patch)
treed01a6d18389f42e02aebb8458c8a0ef5e682489f
parent53e07a4ebb94ae613b552af1d6e3d0ff7ee5cdff (diff)
change the syntax of `cols_order` in conf
-rw-r--r--Cargo.lock2
-rw-r--r--src/command/panel_input.rs1
-rw-r--r--src/conf/conf.rs19
-rw-r--r--src/conf/default_conf.rs23
-rw-r--r--src/conf/toml.rs49
-rw-r--r--src/display/col.rs51
-rw-r--r--src/display/crop_writer.rs18
-rw-r--r--src/display/displayable_tree.rs16
-rw-r--r--src/display/filling.rs63
-rw-r--r--src/display/mod.rs22
-rw-r--r--src/errors.rs1
-rw-r--r--src/hex/hex_view.rs4
-rw-r--r--src/preview/preview_state.rs4
-rw-r--r--src/syntactic/syntactic_view.rs4
-rw-r--r--src/verb/verb_conf.rs8
-rw-r--r--website/docs/conf_file.md39
16 files changed, 218 insertions, 106 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 70846a3..69b9049 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1460,8 +1460,6 @@ dependencies = [
[[package]]
name = "termimad"
version = "0.8.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "695a09c526e20acf459e522670afc5486eeb1c9cf2823515ed4ad4f8f48c8f30"
dependencies = [
"crossbeam",
"crossterm",
diff --git a/src/command/panel_input.rs b/src/command/panel_input.rs
index f8e3af0..c8081a7 100644
--- a/src/command/panel_input.rs
+++ b/src/command/panel_input.rs
@@ -57,6 +57,7 @@ impl PanelInput {
/// consume the event to
/// - maybe change the input
/// - build a command
+ /// then redraw the input field
pub fn on_event(
&mut self,
w: &mut W,
diff --git a/src/conf/conf.rs b/src/conf/conf.rs
index 8c34d2b..9a36b58 100644
--- a/src/conf/conf.rs
+++ b/src/conf/conf.rs
@@ -87,7 +87,12 @@ impl Conf {
/// (i.e. this function is called before or after the terminal alternation)
pub fn read_file(&mut self, filepath: &Path) -> Result<(), ConfError> {
let data = fs::read_to_string(filepath)?;
- let root: Value = data.parse::<Value>()?;
+ let root: toml::value::Table = match data.parse::<Value>()? {
+ Value::Table(tbl) => tbl,
+ _ => {
+ return Err(ConfError::Invalid {});
+ }
+ };
// reading default flags
if let Some(s) = string_field(&root, "default_flags") {
// it's additive because another config file may have
@@ -104,9 +109,15 @@ impl Conf {
self.disable_mouse_capture = !mouse_capture;
}
// cols order
- self.cols_order = string_field(&root, "cols_order")
- .map(|s| Col::parse_cols(&s))
- .transpose()?;
+ if let Some(s) = string_field(&root, "cols_order") {
+ // old format, with each char being a col, for example
+ // `cols_order = "gbpdscn"`
+ self.cols_order = Some(Col::parse_cols_single_str(&s)?);
+ } else if let Some(arr) = string_array_field(&root, "cols_order") {
+ // new format, where each col is a string, for example
+ // `cols_order = ["branch", "size" ..., "name"]`
+ self.cols_order = Some(Col::parse_cols(&arr)?);
+ }
// 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 6375e83..bfccebb 100644
--- a/src/conf/default_conf.rs
+++ b/src/conf/default_conf.rs
@@ -52,17 +52,20 @@ default_flags = ""
###############################################################
# 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, if specified, must be a permutation of the following
+# array. You should keep the name at the end as it has a variable
+# length.
#
-# cols_order = "gbdscn"
+# cols_order = [
+# "mark",
+# "git",
+# "branch",
+# "permission",
+# "date",
+# "size",
+# "count",
+# "name",
+# ]
###############################################################
# True Colors
diff --git a/src/conf/toml.rs b/src/conf/toml.rs
index 2827910..f734352 100644
--- a/src/conf/toml.rs
+++ b/src/conf/toml.rs
@@ -1,24 +1,47 @@
-
use {
- toml::Value,
+ toml::{
+ value::Table,
+ Value,
+ },
};
-pub fn string_field(value: &Value, field_name: &str) -> Option<String> {
- if let Value::Table(tbl) = value {
- if let Some(fv) = tbl.get(field_name) {
- if let Some(s) = fv.as_str() {
- return Some(s.to_string());
+
+/// try to read the field_name property of the given value, as a string
+pub fn string_field(tbl: &Table, field_name: &str) -> Option<String> {
+ tbl.get(field_name)
+ .and_then(|fv| fv.as_str())
+ .map(|s| s.to_string())
+}
+
+/// try to read the field_name property of the given value, as an array of strings.
+/// Return None if the type isn't compatible
+pub fn string_array_field(tbl: &Table, field_name: &str) -> Option<Vec<String>> {
+ if let Some(fv) = tbl.get(field_name) {
+ if let Value::Array(arr_val) = fv {
+ let mut arr = Vec::new();
+ for v in arr_val {
+ match v.as_str() {
+ Some(s) => {
+ arr.push(s.to_string());
+ }
+ None => {
+ return None; // non matching value
+ }
+ }
}
+ return Some(arr);
}
}
None
+
}
-pub fn bool_field(value: &Value, field_name: &str) -> Option<bool> {
- if let Value::Table(tbl) = value {
- if let Some(Value::Boolean(b)) = tbl.get(field_name) {
- return Some(*b);
- }
+/// try to read the field_name property of the given value, as a boolean
+/// (only read it if it's a proper toml boolean, doesn't try to do hazardous
+/// string to bool conversions)
+pub fn bool_field(tbl: &Table, field_name: &str) -> Option<bool> {
+ match tbl.get(field_name) {
+ Some(Value::Boolean(b)) => Some(*b),
+ _ => None,
}
- None
}
diff --git a/src/display/col.rs b/src/display/col.rs
index 54f1f9f..ec0425e 100644
--- a/src/display/col.rs
+++ b/src/display/col.rs
@@ -2,6 +2,7 @@ use {
crate::{
errors::ConfError,
},
+ std::str::FromStr,
};
// number of columns in enum
@@ -35,23 +36,27 @@ pub enum Col {
Name,
}
-impl Col {
- pub fn parse(c: char) -> Result<Self, ConfError> {
- Ok(match c {
- 'm' => Self::Mark,
- 'g' => Self::Git,
- 'b' => Self::Branch,
- 'd' => Self::Date,
- 's' => Self::Size,
- 'c' => Self::Count,
- 'n' => Self::Name,
- _ => {
- return Err(ConfError::InvalidCols {
- details: format!("column not recognized : {}", c),
- });
- }
- })
+impl FromStr for Col {
+ type Err = ConfError;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let s = s.to_lowercase();
+ match s.as_ref() {
+ "m" | "mark" => Ok(Self::Mark),
+ "g" | "git" => Ok(Self::Git),
+ "b" | "branch" => Ok(Self::Branch),
+ "p" | "permission" => Ok(Self::Permission),
+ "d" | "date" => Ok(Self::Date),
+ "s" | "size" => Ok(Self::Size),
+ "c" | "count" => Ok(Self::Count),
+ "n" | "name" => Ok(Self::Name),
+ _ => Err(ConfError::InvalidCols {
+ details: format!("column not recognized : {}", s),
+ })
+ }
}
+}
+
+impl Col {
pub fn index_in(self, cols: &Cols) -> Option<usize> {
for (idx, col) in cols.iter().enumerate() {
if *col==self {
@@ -62,22 +67,28 @@ impl Col {
}
/// 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> {
+ pub fn parse_cols(arr: &Vec<String>) -> Result<Cols, ConfError> {
let mut cols = DEFAULT_COLS;
- for (idx, c) in s.chars().enumerate() {
+ for (idx, s) in arr.iter().enumerate() {
if idx >= COLS_COUNT {
return Err(ConfError::InvalidCols {
- details: format!("too long: {:?}", s),
+ details: format!("too long: {:?}", arr),
});
}
// we swap the cols, to ensure both keeps being present
- let col = Col::parse(c)?;
+ let col = Col::from_str(s)?;
let dest_idx = col.index_in(&cols).unwrap(); // can't be none by construct
cols[dest_idx] = cols[idx];
cols[idx] = col;
}
+ debug!("cols from conf = {:?}", cols);
Ok(cols)
}
+ /// 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_single_str(s: &str) -> Result<Cols, ConfError> {
+ Self::parse_cols(&s.chars().map(|c| String::from(c)).collect())
+ }
}
pub type Cols = [Col;COLS_COUNT];
diff --git a/src/display/crop_writer.rs b/src/display/crop_writer.rs
index 9fb431a..927b7dd 100644
--- a/src/display/crop_writer.rs
+++ b/src/display/crop_writer.rs
@@ -1,4 +1,5 @@
use {
+ super::Filling,
crossterm::{
QueueableCommand,
style::Print,
@@ -108,19 +109,12 @@ where
pub fn queue_bg(&mut self, cs: &CompoundStyle) -> Result<()> {
cs.queue_bg(self.w)
}
- pub fn fill(&mut self, cs: &CompoundStyle, filling: &'static str) -> Result<()> {
+ pub fn fill(&mut self, cs: &CompoundStyle, filling: &'static Filling) -> Result<()> {
self.repeat(cs, filling, self.allowed)
}
- pub fn repeat(&mut self, cs: &CompoundStyle, filling: &'static str, mut len: usize) -> Result<()> {
- loop {
- let slice_len = len.min(self.allowed).min(filling.len());
- if slice_len == 0 {
- break;
- }
- cs.queue_str(self.w, &filling[0..slice_len])?;
- self.allowed -= slice_len;
- len -= slice_len;
- }
- Ok(())
+ pub fn repeat(&mut self, cs: &CompoundStyle, filling: &'static Filling, mut len: usize) -> Result<()> {
+ len = len.min(self.allowed);
+ self.allowed -= len;
+ filling.queue_styled(self.w, cs, len)
}
}
diff --git a/src/display/displayable_tree.rs b/src/display/displayable_tree.rs
index c31b2dd..03b1922 100644
--- a/src/display/displayable_tree.rs
+++ b/src/display/displayable_tree.rs
@@ -4,7 +4,7 @@ use {
Cols,
CropWriter,
GitStatusDisplay,
- LONG_SPACE, LONG_BRANCH,
+ SPACE_FILLING, BRANCH_FILLING,
MatchedString,
},
crate::{
@@ -351,7 +351,7 @@ impl<'s, 't> DisplayableTree<'s, 't> {
} else {
&self.skin.default
};
- cw.fill(style, LONG_SPACE)?;
+ cw.fill(style, &SPACE_FILLING)?;
}
Ok(())
}
@@ -463,13 +463,13 @@ impl<'s, 't> DisplayableTree<'s, 't> {
}
};
// void: intercol & replacing missing cells
- let (void_base_style, void) = if in_branch && void_len > 2 {
- (&self.skin.tree, LONG_BRANCH)
+ if in_branch && void_len > 2 {
+ cond_bg!(void_style, self, selected, &self.skin.tree);
+ cw.repeat(void_style, &BRANCH_FILLING, void_len)?;
} else {
- (&self.skin.default, LONG_SPACE)
- };
- cond_bg!(void_style, self, selected, void_base_style);
- cw.repeat(void_style, void, void_len)?;
+ cond_bg!(void_style, self, selected, &self.skin.default);
+ cw.repeat(void_style, &SPACE_FILLING, void_len)?;
+ }
}
if cw.allowed > 8 && pattern_object.content {
diff --git a/src/display/filling.rs b/src/display/filling.rs
new file mode 100644
index 0000000..705977b
--- /dev/null
+++ b/src/display/filling.rs
@@ -0,0 +1,63 @@
+
+use {
+ crossterm::{
+ QueueableCommand,
+ style::Print,
+ },
+ termimad::{
+ CompoundStyle,
+ Result,
+ },
+};
+
+const FILLING_STRING_CHAR_LEN: usize = 1000;
+
+pub struct Filling {
+ filling_string: String,
+ char_size: usize,
+}
+
+impl Filling {
+ // TODO as soon as const fn are capable enough, this should be const
+ // to allow normal static fillings
+ pub fn from_char(filling_char: char) -> Self {
+ let char_size = String::from(filling_char).len();
+ let mut filling_string = String::with_capacity(char_size*FILLING_STRING_CHAR_LEN);
+ for _ in 0..FILLING_STRING_CHAR_LEN {
+ filling_string.push(filling_char);
+ }
+ Self {
+ filling_string,
+ char_size,
+ }
+ }
+ pub fn queue_unstyled<W>(
+ &self,
+ w: &mut W,
+ mut len: usize,
+ ) -> Result<()>
+ where W: std::io::Write
+ {
+ while len > 0 {
+ let sl = len.min(FILLING_STRING_CHAR_LEN);
+ w.queue(Print(&self.filling_string[0..sl*self.char_size]))?;
+ len -= sl;
+ }
+ Ok(())
+ }
+ pub fn queue_styled<W>(
+ &self,
+ w: &mut W,
+ cs: &CompoundStyle,
+ mut len: usize,
+ ) -> Result<()>
+ where W: std::io::Write
+ {
+ while len > 0 {
+ let sl = len.min(FILLING_STRING_CHAR_LEN);
+ cs.queue_str(w, &self.filling_string[0..sl*self.char_size])?;
+ len -= sl;
+ }
+ Ok(())
+ }
+}
diff --git a/src/display/mod.rs b/src/display/mod.rs
index da1e588..ac8e70d 100644
--- a/src/display/mod.rs
+++ b/src/display/mod.rs
@@ -24,8 +24,9 @@ mod areas;
mod col;
mod crop_writer;
mod displayable_tree;
-pub mod flags_display;
+mod filling;
mod git_status_display;
+pub mod flags_display;
pub mod status_line;
mod matched_string;
mod screen;
@@ -38,6 +39,7 @@ pub use {
col::{Col, Cols, DEFAULT_COLS},
crop_writer::CropWriter,
displayable_tree::DisplayableTree,
+ filling::*,
git_status_display::GitStatusDisplay,
matched_string::MatchedString,
screen::Screen,
@@ -49,7 +51,6 @@ use {
crossterm::{
style::{
Color,
- Print,
SetBackgroundColor,
},
QueueableCommand,
@@ -61,9 +62,11 @@ pub use {
permissions::PermWriter,
};
-pub static LONG_SPACE: &str = " ";
-pub static LONG_BRANCH: &str = "───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────";
+lazy_static! {
+ pub static ref SPACE_FILLING: Filling = Filling::from_char(' ');
+ pub static ref BRANCH_FILLING: Filling = Filling::from_char('─');
+}
/// if true then the status of a panel covers the whole width
/// of the terminal (over the other panels)
@@ -80,17 +83,10 @@ pub fn writer() -> W {
pub fn fill_bg(
w: &mut W,
- mut len: usize,
+ len: usize,
bg: Color,
) -> Result<(), ProgramError> {
w.queue(SetBackgroundColor(bg))?;
- loop {
- let slice_len = len.min(len).min(LONG_SPACE.len());
- if slice_len == 0 {
- break;
- }
- w.queue(Print(&LONG_SPACE[0..slice_len]))?;
- len -= slice_len;
- }
+ SPACE_FILLING.queue_unstyled(w, len)?;
Ok(())
}
diff --git a/src/errors.rs b/src/errors.rs
index f31bee9..9bd7fe6 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -35,6 +35,7 @@ custom_error! {pub TreeBuildError
custom_error! {pub ConfError
Io {source: io::Error} = "unable to read from the file: {}",
Toml {source: toml::de::Error} = "unable to parse TOML: {}",
+ Invalid = "unexpected conf structure", // not expected
MissingField {txt: String} = "missing field in conf",
InvalidVerbInvocation {invocation: String} = "invalid verb invocation: {}",
InvalidVerbConf {details: String} = "invalid verb conf: {}",
diff --git a/src/hex/hex_view.rs b/src/hex/hex_view.rs
index b645ff3..75005a8 100644
--- a/src/hex/hex_view.rs
+++ b/src/hex/hex_view.rs
@@ -5,7 +5,7 @@ use {
},
crate::{
command::{ScrollCommand},
- display::{CropWriter, LONG_SPACE, Screen, W},
+ display::{CropWriter, SPACE_FILLING, Screen, W},
errors::ProgramError,
skin::PanelSkin,
},
@@ -203,7 +203,7 @@ impl HexView {
}
}
}
- cw.fill(&styles.default, LONG_SPACE)?;
+ cw.fill(&styles.default, &SPACE_FILLING)?;
if is_thumb(y, scrollbar) {
w.queue(SetForegroundColor(scrollbar_fg))?;
w.queue(Print('▐'))?;
diff --git a/src/preview/preview_state.rs b/src/preview/preview_state.rs
index 4cff8db..68ef09e 100644
--- a/src/preview/preview_state.rs
+++ b/src/preview/preview_state.rs
@@ -3,7 +3,7 @@ use {
crate::{
app::*,
command::{Command, ScrollCommand, TriggerType},
- display::{CropWriter, LONG_SPACE, Screen, W},
+ display::{CropWriter, SPACE_FILLING, Screen, W},
errors::ProgramError,
flag::Flag,
pattern::InputPattern,
@@ -217,7 +217,7 @@ impl AppState for PreviewState {
cw.allowed as u16,
1,
);
- cw.fill(&styles.default, LONG_SPACE)?;
+ cw.fill(&styles.default, &SPACE_FILLING)?;
let preview = self.filtered_preview.as_mut().unwrap_or(&mut self.preview);
preview.display_info(w, screen, panel_skin, &info_area)?;
preview.display(w, screen, panel_skin, &self.preview_area, con)
diff --git a/src/syntactic/syntactic_view.rs b/src/syntactic/syntactic_view.rs
index aa707f4..b2aa8ff 100644
--- a/src/syntactic/syntactic_view.rs
+++ b/src/syntactic/syntactic_view.rs
@@ -3,7 +3,7 @@ use {
crate::{
app::{AppContext, LineNumber},
command::{ScrollCommand},
- display::{CropWriter, LONG_SPACE, Screen, W},
+ display::{CropWriter, SPACE_FILLING, Screen, W},
errors::ProgramError,
pattern::{InputPattern, NameMatch},
skin::PanelSkin,
@@ -372,7 +372,7 @@ impl SyntacticView {
}
cw.fill(
if selected { &styles.selected_line } else { &styles.preview },
- LONG_SPACE,
+ &SPACE_FILLING,
)?;
w.queue(SetBackgroundColor(bg))?;
if is_thumb(y, scrollbar) {
diff --git a/src/verb/verb_conf.rs b/src/verb/verb_conf.rs
index c7e7776..585e674 100644
--- a/src/verb/verb_conf.rs
+++ b/src/verb/verb_conf.rs
@@ -16,6 +16,14 @@ use {
impl TryFrom<&Value> for Verb {
type Error = ConfError;
fn try_from(verb_value: &Value) -> Result<Self, Self::Error> {
+ let verb_value = match verb_value {
+ Value::Table(tbl) => tbl,
+ _ => {
+ return Err(ConfError::InvalidVerbConf {
+ details: "unexpected verb conf structure".to_string(),
+ });
+ }
+ };
let invocation = string_field(verb_value, "invocation");
let key = string_field(verb_value, "key")
.map(|s| keys::parse_key(&s))
diff --git a/website/docs/conf_file.md b/website/docs/conf_file.md
index de8d767..518210f 100644
--- a/website/docs/conf_file.md
+++ b/website/docs/conf_file.md
@@ -81,30 +81,33 @@ show_selection_mark = true
# Columns order
-You may change the order of file attributes in file lists.
+You may change the order of file attributes in file lists:
-The `cols_order` property, if specified, must be a permutation of `"gbpdscn"` where every char denotes a column:
+* mark: a small triangle flagging the selected line
+* git : Git file info
+* branch : shows the depth and parent in the tree
+* permission : mode, user, group
+* date : last modification date
+* size : ISO size (and size bar when sorting)
+* count : number of files in directories
+* name : file name
-* 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
-
-The default value is
-
-```toml
-cols_order = "gscpdbn"
-```
-If you prefer to have the branches left of the tree (as was the default in broot prior 0.18.1) you can use
+For example, if you prefer to have the branches left of the tree (as was the default in broot prior 0.18.1) you can use
```toml
-cols_order = "gbpdscn"
+cols_order = [
+ "mark",
+ "git",
+ "branch",
+ "permission",
+ "date",
+ "size",
+ "count",
+ "name",
+]
```
-The `n` column should be kept at end as it's the only one with a variable size.
+The name should be kept at end as it's the only one with a variable size.
# Colors by file extension