diff options
author | Canop <cano.petrole@gmail.com> | 2019-01-07 20:31:08 +0100 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2019-01-07 20:31:08 +0100 |
commit | dbfe0bc826a52af41c5957a7af5b596017f6ae45 (patch) | |
tree | 3dc66f5a3655003ef37bd8fc4dec799bbed4f46d | |
parent | 7c6a719ae2b37becad912c977abc4aaaa5f682f4 (diff) |
filtering pattern kept after verb executionv0.4.2
(when the verb doesn't quit broot)
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/app.rs | 2 | ||||
-rw-r--r-- | src/browser_states.rs | 9 | ||||
-rw-r--r-- | src/commands.rs | 88 | ||||
-rw-r--r-- | src/conf.rs | 12 | ||||
-rw-r--r-- | src/flat_tree.rs | 8 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/tree_options.rs | 2 | ||||
-rw-r--r-- | src/tree_views.rs | 66 | ||||
-rw-r--r-- | src/verbs.rs | 14 |
11 files changed, 139 insertions, 68 deletions
@@ -31,7 +31,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "broot" -version = "0.4.1" +version = "0.4.2" dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "custom_error 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1,6 +1,6 @@ [package] name = "broot" -version = "0.4.1" +version = "0.4.2" authors = ["dystroy <denys.seguret@gmail.com>"] repository = "https://github.com/Canop/broot" edition = "2018" @@ -152,7 +152,7 @@ impl App { } AppStateCmdResult::NewState(boxed_state) => { self.push(boxed_state); - cmd = Command::new(); + cmd = cmd.pop_verb(); self.state().write_status(&mut screen, &cmd, con)?; } AppStateCmdResult::PopState => { diff --git a/src/browser_states.rs b/src/browser_states.rs index 3185e64..00d81f7 100644 --- a/src/browser_states.rs +++ b/src/browser_states.rs @@ -27,13 +27,15 @@ pub struct BrowserState { } impl BrowserState { - pub fn new(path: PathBuf, options: TreeOptions, tl: &TaskLifetime) -> Option<BrowserState> { - let builder = TreeBuilder::from(path, options.clone()); + pub fn new(path: PathBuf, mut options: TreeOptions, tl: &TaskLifetime) -> Option<BrowserState> { + let pending_pattern = options.pattern; + options.pattern = None; + let builder = TreeBuilder::from(path, options); match builder.build(screens::max_tree_height() as usize, tl) { Some(tree) => Some(BrowserState { tree, filtered_tree: None, - pending_pattern: None, + pending_pattern, }), None => None, // interrupted } @@ -101,6 +103,7 @@ impl AppState for BrowserState { let tl = TaskLifetime::unlimited(); AppStateCmdResult::from_optional_state(BrowserState::new( tree.selected_line().path.clone(), + // tree.options.clone(), tree.options.without_pattern(), &tl, )) diff --git a/src/commands.rs b/src/commands.rs index 7907e2c..5ad96f1 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -4,50 +4,73 @@ use termion::event::Key; /// A command is the parsed representation of what the user types /// in the input. It's independant of the state of the application /// (verbs arent checked at this point) -#[derive(Debug)] -pub enum Action { - MoveSelection(i32), // up (neg) or down (positive) in the list - ScrollPage(i32), // in number of pages, not lines - OpenSelection, // open the selected line (which can't be the root by construct) - VerbEdit(String), // verb, unfinished - Verb(String), // verb - PatternEdit(String), // a pattern being edited - Back, // back to last app state, or clear pattern - Next, - Help(String), - Unparsed, // or unparsable + +struct CommandParts { + pattern: Option<String>, + verb: Option<String>, } -impl Action { - pub fn from(raw: &str, finished: bool) -> Action { +impl CommandParts { + fn from(raw: &str) -> CommandParts { + let mut cp = CommandParts { + pattern: None, + verb: None, + }; lazy_static! { static ref RE: Regex = Regex::new( r"(?x) ^ (?P<pattern>[^\s/:]*) - (?:[\s:]+(?P<verb>\w*))? + (?:[\s:]+(?P<verb>\S*))? $ " ) .unwrap(); } if let Some(c) = RE.captures(raw) { - if let Some(verb) = c.name("verb") { - return match finished { - false => Action::VerbEdit(String::from(verb.as_str())), - true => Action::Verb(String::from(verb.as_str())), - }; - } if let Some(pattern) = c.name("pattern") { - let pattern = pattern.as_str(); - return match finished { - false => Action::PatternEdit(String::from(pattern)), - true => Action::OpenSelection, - }; + cp.pattern = Some(String::from(pattern.as_str())); + } + if let Some(verb) = c.name("verb") { + cp.verb = Some(String::from(verb.as_str())); } } else { warn!("unexpected lack of capture"); } + cp + } +} + +#[derive(Debug)] +pub enum Action { + MoveSelection(i32), // up (neg) or down (positive) in the list + ScrollPage(i32), // in number of pages, not lines + OpenSelection, // open the selected line (which can't be the root by construct) + VerbEdit(String), // verb, unfinished + Verb(String), // verb + PatternEdit(String), // a pattern being edited + Back, // back to last app state, or clear pattern + Next, + Help(String), + Unparsed, // or unparsable +} + +impl Action { + pub fn from(raw: &str, finished: bool) -> Action { + let cp = CommandParts::from(raw); + if let Some(verb) = cp.verb { + return match finished { + false => Action::VerbEdit(String::from(verb.as_str())), + true => Action::Verb(String::from(verb.as_str())), + }; + } + if let Some(pattern) = cp.pattern { + let pattern = pattern.as_str(); + return match finished { + false => Action::PatternEdit(String::from(pattern)), + true => Action::OpenSelection, + }; + } Action::Unparsed } } @@ -65,6 +88,19 @@ impl Command { action: Action::Unparsed, } } + // build a new command, after execution of a verb + // (in the future the new action might be built by the state + // which would be cleaner) + pub fn pop_verb(&self) -> Command { + let mut c = Command::new(); + let cp = CommandParts::from(&self.raw); + if cp.verb.is_some() { + if let Some(pat) = cp.pattern { + c.raw = pat; + } + } + c + } pub fn add_key(&mut self, key: Key) { match key { Key::Char('\t') => { diff --git a/src/conf.rs b/src/conf.rs index fac9e61..3b9970c 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -4,6 +4,8 @@ use std::path::{Path, PathBuf}; use std::result::Result; use toml::{self, Value}; +/// manage reading the verb shortcuts from the configuration file, +/// initializing if if it doesn't yet exist use custom_error::custom_error; use directories::ProjectDirs; @@ -96,7 +98,7 @@ const DEFAULT_CONF_FILE: &str = r#" # ":back" : reverts to the previous state, or quit the application if it's the first one (mapped to <esc>) # ":cd" : changes directory (see https://github.com/Canop/broot) # ":print_path" : outputs the path to stdout or to a file provided with --out -# ":focus" : displays the tree of that directory (mapped to <enter> on directories) +# ":focus" : displays the tree of that directory, keeps the current search pattern # ":open" : tries to open the file according to OS settings (e.g. using xdg-open) (mapped to <enter> on files) # ":parent" : moves to the parent directory # ":quit" : quits the application @@ -113,7 +115,7 @@ execution = ":cd" [[verbs]] name = "focus" -invocation = "f" +invocation = "g" execution = ":focus" [[verbs]] @@ -142,12 +144,6 @@ invocation = "o" execution = ":open" [[verbs]] -# this is an example of a very specific verb -name = "geany" -invocation = "g" -execution = "/usr/bin/geany {file}" - -[[verbs]] name = "toggle sizes" invocation = "s" execution = ":toggle_sizes" diff --git a/src/flat_tree.rs b/src/flat_tree.rs index 90b83c4..5914410 100644 --- a/src/flat_tree.rs +++ b/src/flat_tree.rs @@ -27,9 +27,9 @@ pub struct TreeLine { pub unlisted: usize, // number of not listed childs (Dir) or brothers (Pruning) pub score: i32, // 0 if there's no pattern pub size: Option<Size>, // None when not measured - pub mode: u32, // unix file mode - pub uid: u32, // unix user id - pub gid: u32, // unix group id + pub mode: u32, // unix file mode + pub uid: u32, // unix user id + pub gid: u32, // unix group id } #[derive(Debug)] @@ -194,7 +194,7 @@ impl Tree { if dy < 0 && sel < self.scroll + 5 { self.scroll = (self.scroll + 2 * dy).max(0); } else if dy > 0 && l > page_height && sel > self.scroll + page_height - 5 { - self.scroll = self.scroll + 2 * dy; + self.scroll += 2 * dy; } } pub fn try_scroll(&mut self, dy: i32, page_height: i32) { diff --git a/src/main.rs b/src/main.rs index e74cb42..7b046c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,7 @@ use crate::verbs::VerbStore; fn get_cli_args<'a>() -> clap::ArgMatches<'a> { clap::App::new("broot") - .version("0.4.1") + .version("0.4.2") .author("dystroy <denys.seguret@gmail.com>") .about("Balanced tree view + fuzzy search + BFS + customizable launcher") .arg(clap::Arg::with_name("root").help("sets the root directory")) diff --git a/src/tree_options.rs b/src/tree_options.rs index de71ba2..02736d8 100644 --- a/src/tree_options.rs +++ b/src/tree_options.rs @@ -12,7 +12,7 @@ pub enum OptionBool { impl FromStr for OptionBool { type Err = ProgramError; fn from_str(s: &str) -> Result<OptionBool, ProgramError> { - match s.as_ref() { + match s { "auto" => Ok(OptionBool::Auto), "yes" => Ok(OptionBool::Yes), "no" => Ok(OptionBool::No), diff --git a/src/tree_views.rs b/src/tree_views.rs index d95a3f8..0b59e48 100644 --- a/src/tree_views.rs +++ b/src/tree_views.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use std::io::{self, Write}; -use termion::{color, style}; -use users::{Users, Groups, UsersCache}; use std::sync::Mutex; +use termion::{color, style}; +use users::{Groups, Users, UsersCache}; use crate::flat_tree::{LineType, Tree, TreeLine}; use crate::patterns::Pattern; @@ -34,7 +34,8 @@ impl TreeView for Screen { max_user_name_len = max_user_name_len.max(user.name().to_string_lossy().len()); } if let Some(group) = users_cache.get_group_by_gid(line.uid) { - max_group_name_len = max_group_name_len.max(group.name().to_string_lossy().len()); + max_group_name_len = + max_group_name_len.max(group.name().to_string_lossy().len()); } } } @@ -111,22 +112,58 @@ impl TreeView for Screen { self.stdout, "{} {}{}{}{}{}{}{}{}{}", color::Fg(color::AnsiValue::grayscale(15)), - if (line.mode & (1<<8))!=0 { 'r' } else { '-' }, - if (line.mode & (1<<7))!=0 { 'w' } else { '-' }, - if (line.mode & (1<<6))!=0 { 'x' } else { '-' }, - if (line.mode & (1<<5))!=0 { 'r' } else { '-' }, - if (line.mode & (1<<4))!=0 { 'w' } else { '-' }, - if (line.mode & (1<<3))!=0 { 'x' } else { '-' }, - if (line.mode & (1<<2))!=0 { 'r' } else { '-' }, - if (line.mode & (1<<1))!=0 { 'w' } else { '-' }, - if (line.mode & (1<<0))!=0 { 'x' } else { '-' }, + if (line.mode & (1 << 8)) != 0 { + 'r' + } else { + '-' + }, + if (line.mode & (1 << 7)) != 0 { + 'w' + } else { + '-' + }, + if (line.mode & (1 << 6)) != 0 { + 'x' + } else { + '-' + }, + if (line.mode & (1 << 5)) != 0 { + 'r' + } else { + '-' + }, + if (line.mode & (1 << 4)) != 0 { + 'w' + } else { + '-' + }, + if (line.mode & (1 << 3)) != 0 { + 'x' + } else { + '-' + }, + if (line.mode & (1 << 2)) != 0 { + 'r' + } else { + '-' + }, + if (line.mode & (1 << 1)) != 0 { + 'w' + } else { + '-' + }, + if (line.mode & (1 << 0)) != 0 { + 'x' + } else { + '-' + }, )?; if let Some(user) = users_cache.get_user_by_uid(line.uid) { write!( self.stdout, " {:w$}", user.name().to_string_lossy(), - w=max_user_name_len, + w = max_user_name_len, )?; } if let Some(group) = users_cache.get_group_by_gid(line.uid) { @@ -134,7 +171,7 @@ impl TreeView for Screen { self.stdout, " {:w$} ", group.name().to_string_lossy(), - w=max_group_name_len, + w = max_group_name_len, )?; } } else { @@ -144,7 +181,6 @@ impl TreeView for Screen { color::Fg(color::AnsiValue::grayscale(5)), color::Fg(color::Reset), )?; - } } let selected = line_index == tree.selection; diff --git a/src/verbs.rs b/src/verbs.rs index abe0605..56d8858 100644 --- a/src/verbs.rs +++ b/src/verbs.rs @@ -48,8 +48,8 @@ impl VerbExecutor for BrowserState { Ok(match verb.exec_pattern.as_ref() { ":back" => AppStateCmdResult::PopState, ":focus" => { - let path = self.tree.selected_line().path.clone(); - let options = self.tree.options.clone(); + let path = tree.selected_line().path.clone(); + let options = tree.options.clone(); AppStateCmdResult::from_optional_state(BrowserState::new( path, options, @@ -57,7 +57,7 @@ impl VerbExecutor for BrowserState { )) } ":toggle_hidden" => { - let mut options = self.tree.options.clone(); + let mut options = tree.options.clone(); options.show_hidden = !options.show_hidden; AppStateCmdResult::from_optional_state(BrowserState::new( self.tree.root().clone(), @@ -66,7 +66,7 @@ impl VerbExecutor for BrowserState { )) } ":toggle_git_ignore" => { - let mut options = self.tree.options.clone(); + let mut options = tree.options.clone(); options.respect_git_ignore = match options.respect_git_ignore { OptionBool::Auto => { if tree.nb_gitignored > 0 { @@ -86,7 +86,7 @@ impl VerbExecutor for BrowserState { )) } ":toggle_files" => { - let mut options = self.tree.options.clone(); + let mut options = tree.options.clone(); options.only_folders = !options.only_folders; AppStateCmdResult::from_optional_state(BrowserState::new( self.tree.root().clone(), @@ -95,7 +95,7 @@ impl VerbExecutor for BrowserState { )) } ":toggle_perm" => { - let mut options = self.tree.options.clone(); + let mut options = tree.options.clone(); options.show_permissions = !options.show_permissions; AppStateCmdResult::from_optional_state(BrowserState::new( self.tree.root().clone(), @@ -104,7 +104,7 @@ impl VerbExecutor for BrowserState { )) } ":toggle_sizes" => { - let mut options = self.tree.options.clone(); + let mut options = tree.options.clone(); options.show_sizes = !options.show_sizes; AppStateCmdResult::from_optional_state(BrowserState::new( self.tree.root().clone(), |