diff --git a/CHANGELOG.md b/CHANGELOG.md index 29fbd9c0..d3299f65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ### next version +#### Major feature: new input syntax - Breaking Change +- new search modes: fuzzy or regex on sub-paths (the path starting from the displayed root) +- it's possible to configure how search modes are selected in config +#### Minor changes: +- tab goes to next direct match when there's no verb in input - Fix #234 ### v0.14.2 - 2020-06-01 diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs index c06ab01e..5bbb7c83 100644 --- a/src/browser/browser_state.rs +++ b/src/browser/browser_state.rs @@ -427,6 +427,14 @@ impl AppState for BrowserState { self.displayed_tree_mut().move_selection(-1, page_height); AppStateCmdResult::Keep } + Internal::previous_match => { + self.displayed_tree_mut().try_select_previous_match(); + AppStateCmdResult::Keep + } + Internal::next_match => { + self.displayed_tree_mut().try_select_next_match(); + AppStateCmdResult::Keep + } Internal::page_down => { let tree = self.displayed_tree_mut(); if page_height < tree.lines.len() as i32 { diff --git a/src/command/event.rs b/src/command/event.rs index 819760a6..7cb9462f 100644 --- a/src/command/event.rs +++ b/src/command/event.rs @@ -117,41 +117,44 @@ impl PanelInput { // tab completion if key == keys::TAB { - let parts_before_cycle; - let completable_parts = if let Some(s) = &self.input_before_cycle { - parts_before_cycle = CommandParts::from(s); - &parts_before_cycle - } else { - &parts - }; - let completions = Completions::for_input(completable_parts, con, state); - let added = match completions { - Completions::None => { - debug!("nothing to complete!"); // where to tell this ? input field or status ? - self.tab_cycle_count = 0; - self.input_before_cycle = None; - None - } - Completions::Common(completion) => { - self.tab_cycle_count = 0; - //self.input_before_cycle = Some(raw.to_string()); - Some(completion) - } - Completions::List(mut completions) => { - let idx = self.tab_cycle_count % completions.len(); - if self.tab_cycle_count == 0 { - self.input_before_cycle = Some(raw.to_string()); + if parts.verb_invocation.is_some() { + let parts_before_cycle; + let completable_parts = if let Some(s) = &self.input_before_cycle { + parts_before_cycle = CommandParts::from(s); + &parts_before_cycle + } else { + &parts + }; + let completions = Completions::for_input(completable_parts, con, state); + let added = match completions { + Completions::None => { + debug!("nothing to complete!"); // where to tell this ? input field or status ? + self.tab_cycle_count = 0; + self.input_before_cycle = None; + None + } + Completions::Common(completion) => { + self.tab_cycle_count = 0; + Some(completion) } - self.tab_cycle_count += 1; - Some(completions.swap_remove(idx)) + Completions::List(mut completions) => { + let idx = self.tab_cycle_count % completions.len(); + if self.tab_cycle_count == 0 { + self.input_before_cycle = Some(raw.to_string()); + } + self.tab_cycle_count += 1; + Some(completions.swap_remove(idx)) + } + }; + if let Some(added) = added { + let mut raw = self.input_before_cycle.as_ref().map_or(raw, |s| s.to_string()); + raw.push_str(&added); + self.input_field.set_content(&raw); + let parts = CommandParts::from(&raw); + return Command::from_parts(&parts, false); + } else { + return Command::None; } - }; - if let Some(added) = added { - let mut raw = self.input_before_cycle.as_ref().map_or(raw, |s| s.to_string()); - raw.push_str(&added); - self.input_field.set_content(&raw); - let parts = CommandParts::from(&raw); - return Command::from_parts(&parts, false); } } else { self.tab_cycle_count = 0; diff --git a/src/keys.rs b/src/keys.rs index 02b503e4..59b649d6 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -89,7 +89,7 @@ pub fn is_reserved(key: KeyEvent) -> bool { //RIGHT => true, // needed for the input field DELETE => true, // needed for the input field ESC => true, // basic navigation - TAB => true, // open tab/panel + //TAB => true, // completion //UP => true, // basic navigation //DOWN => true, // basic navigation _ => false, diff --git a/src/pattern/pattern.rs b/src/pattern/pattern.rs index 4f9f0fe5..87441828 100644 --- a/src/pattern/pattern.rs +++ b/src/pattern/pattern.rs @@ -6,8 +6,6 @@ use { SearchMode, }, crate::{ - app::AppContext, - command::PatternParts, errors::PatternError, }, std::{ diff --git a/src/pattern/search_mode.rs b/src/pattern/search_mode.rs index 59f1a29f..ac0d88cd 100644 --- a/src/pattern/search_mode.rs +++ b/src/pattern/search_mode.rs @@ -63,8 +63,9 @@ impl SearchModeMapEntry { (false, true, false, true) => SearchMode::PathRegex, }; let key = if conf_key.is_empty() || conf_key == "" { - // serde toml parser don't handle correctly empty keys so we accept as + // serde toml parser doesn't handle correctly empty keys so we accept as // alternative the `"" = "fuzzy name"` solution. + // TODO look at issues and/or code in serde-toml None } else if regex!(r"^\w*/$").is_match(conf_key) { Some(conf_key[0..conf_key.len()-1].to_string()) @@ -106,14 +107,11 @@ impl SearchModeMap { self.entries.push(entry); } pub fn search_mode(&self, key: &Option) -> Result { - debug!("searching mode with key {:?}", key); for entry in self.entries.iter().rev() { if entry.key == *key { - debug!("found mode {:?}", entry.mode); return Ok(entry.mode); } } - debug!("map: {:?}", &self); Err(PatternError::InvalidMode { mode: if let Some(key) = key { format!("{}/", key) diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 33c9dd76..6887f7ac 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -146,6 +146,7 @@ impl Tree { loop { self.selection = (self.selection + ((l as i32) + dy) as usize) % l; if self.lines[self.selection].is_selectable() { + debug!("selected line score: {:?}", self.lines[self.selection].score); break; } } diff --git a/src/tree_build/builder.rs b/src/tree_build/builder.rs index 7aa961d0..19493ee8 100644 --- a/src/tree_build/builder.rs +++ b/src/tree_build/builder.rs @@ -134,7 +134,8 @@ impl<'c> TreeBuilder<'c> { } } if let Some(pattern_score) = self.options.pattern.score_of(&name) { - score += pattern_score; + // we dope direct matchs to compensate for depth doping of parent folders + score += pattern_score + 10; } else { has_match = false; } @@ -212,7 +213,6 @@ impl<'c> TreeBuilder<'c> { match bl { BLineResult::Some(child_id) => { if self.blines[child_id].has_match { - // direct match self.blines[bid].has_match = true; has_child_match = true; } diff --git a/src/verb/builtin.rs b/src/verb/builtin.rs index 6b1a9e1f..798aa477 100644 --- a/src/verb/builtin.rs +++ b/src/verb/builtin.rs @@ -59,6 +59,8 @@ pub fn builtin_verbs() -> Vec { .with_shortcut("mvp"), Verb::internal_bang(start_end_panel) .with_control_key('p'), + Verb::internal(next_match) + .with_key(TAB), Verb::internal(open_stay) .with_key(ENTER) .with_shortcut("os"), diff --git a/src/verb/internal.rs b/src/verb/internal.rs index 71941d50..74879f5f 100644 --- a/src/verb/internal.rs +++ b/src/verb/internal.rs @@ -61,11 +61,13 @@ Internals! { line_up: "move one line up", open_stay: "open file or directory according to OS (stay in broot)", open_leave: "open file or directory according to OS (quit broot)", + next_match: "select the next match", page_down: "scroll one page down", page_up: "scroll one page up", parent: "move to the parent directory", panel_left: "focus panel on left", panel_right: "focus panel on right", + previous_match: "select the previous match", print_path: "print path and leaves broot", print_relative_path: "print relative path and leaves broot", print_tree: "print tree and leaves broot",