diff --git a/README.md b/README.md index 91df2869..1bf6461b 100644 --- a/README.md +++ b/README.md @@ -92,12 +92,17 @@ the ones you selected in vim. ## As Interactive Interface -`skim` can invoke other commands dynamically. Normally you would want to integrate it with -[ag](https://github.com/ggreer/the_silver_searcher) or [ack](https://github.com/petdance/ack2) for -searching contents in a project directory: +`skim` can invoke other commands dynamically. Normally you would want to +integrate it with [rg](https://github.com/BurntSushi/ripgrep) +[ag](https://github.com/ggreer/the_silver_searcher) or +[ack](https://github.com/petdance/ack2) for searching contents in a project +directory: ``` +# work with ag sk --ansi -i -c 'ag --color "{}"' +# or with rg +sk --ansi -c 'rg --color=always --line-number "{}"' ``` ![interactive mode demo](https://cloud.githubusercontent.com/assets/1527040/21603930/655d859a-d1db-11e6-9fec-c25099d30a12.gif) @@ -117,7 +122,7 @@ Some common used keybindings. ## Search Syntax -`skim` borrowed `fzf`'s syntax for matching items +`skim` borrowed `fzf`'s syntax for matching items: | Token | Match type | Description | |----------|----------------------------|-----------------------------------| @@ -128,12 +133,24 @@ Some common used keybindings. | `!fire` | inverse-exact-match | items that do not include `fire` | | `!.mp3$` | inverse-suffix-exact-match | items that do not end with `.mp3` | +`skim` also support the combination of tokens. + +- space has the meaning of `AND`. With the term `src main`, `skim` will search + for items that match **both** `src` and `main`. +- ` | ` means `OR` (note the spaces around `|`). With the term `.md$ | + .markdown$`, `skim` will search for items ends with either `.md` or + `.markdown`. +- `OR` have higher precedence. So `readme .md$ | .markdown$` is groupped into + `readme AND (.md$ OR .markdown$)`. + In case that you want to use regular expressions, `skim` provide `regex` mode: ``` sk --regex ``` +You can switch to `regex` mode dynamically by pressing `Ctrl-R` (Rotate Mode). + ## exit code | Exit Code | Meaning | @@ -183,7 +200,7 @@ Specify the bindings with comma seperated pairs(no space allowed), example: | toggle | None | | toggle-all | None | | toggle-down | tab | -| toggle-in | None | +| toggle-interactive | ctrl-q | | toggle-out | None | | toggle-sort | None | | toggle-up | shift-tab | @@ -256,10 +273,16 @@ present the output to you. You can specify the command using the `-c` option: `sk -i -c 'ag --color "{}"'` -In the above example, the replstr `{}` will be replaced with the query you +In the above example, the replace string `{}` will be replaced with the query you type before invoking the command. Use `-I ` to change replstr if you want. +For example, with the input "hello" in interactive mode, `skim` will replace +the above command with `ag --color "hello"` and invoke it. + +If you want to further narrow down the result returned by the command, press +`Ctrl-Q` to toggle interactive mode. + ## Fields support Normally only plugin users need to understand this. diff --git a/src/event.rs b/src/event.rs index 11bcec37..4c662a47 100644 --- a/src/event.rs +++ b/src/event.rs @@ -17,6 +17,7 @@ pub enum Event { EvModelDrawInfo, EvModelNewItem, EvModelNotifyProcessed, + EvModelNotifyMatcherMode, EvModelRestart, EvReaderNewItem, @@ -62,6 +63,7 @@ pub enum Event { EvActToggleAll, EvActToggleDown, EvActToggleIn, + EvActToggleInteractive, EvActToggleOut, EvActToggleSort, EvActToggleUp, @@ -103,6 +105,7 @@ pub fn parse_action(action: &str) -> Option { "toggle-all" => Some(Event::EvActToggleAll), "toggle-down" => Some(Event::EvActToggleDown), "toggle-in" => Some(Event::EvActToggleIn), + "toggle-interactive" => Some(Event::EvActToggleInteractive), "toggle-out" => Some(Event::EvActToggleOut), "toggle-sort" => Some(Event::EvActToggleSort), "toggle-up" => Some(Event::EvActToggleUp), diff --git a/src/input.rs b/src/input.rs index 170416ee..43d5c6c4 100644 --- a/src/input.rs +++ b/src/input.rs @@ -488,6 +488,7 @@ fn get_default_key_map() -> HashMap)> { //ret.insert(Key::AltZ, (Event::EvActToggleAll, None)); ret.insert(Key::Tab, (Event::EvActToggleDown, None)); //ret.insert(Key::AltZ, (Event::EvActToggleIn, None)); + ret.insert(Key::CtrlQ, (Event::EvActToggleInteractive, None)); //ret.insert(Key::AltZ, (Event::EvActToggleOut, None)); //ret.insert(Key::AltZ, (Event::EvActToggleSort, None)); ret.insert(Key::BTab, (Event::EvActToggleUp, None)); diff --git a/src/main.rs b/src/main.rs index de8cddea..0c79d1ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -205,7 +205,7 @@ fn real_main() -> i32 { // reader let (tx_reader, rx_reader) = channel(); let (tx_item, rx_item) = sync_channel(128); - let mut reader = reader::Reader::new(rx_reader, tx_item); + let mut reader = reader::Reader::new(rx_reader, tx_item.clone()); reader.parse_options(&options); thread::spawn(move || { reader.run(); @@ -235,7 +235,7 @@ fn real_main() -> i32 { // Helper functions // light up the fire - let _ = tx_reader.send((EvReaderRestart, Box::new((query.get_cmd(), query.get_query())))); + let _ = tx_reader.send((EvReaderRestart, Box::new((query.get_cmd(), query.get_query(), false)))); let redraw_query = |query: &query::Query| { let _ = tx_model.send((EvModelDrawQuery, Box::new(query.get_print_func()))); @@ -243,7 +243,14 @@ fn real_main() -> i32 { let on_query_change = |query: &query::Query| { // restart the reader with new parameter - let _ = tx_reader.send((EvReaderRestart, Box::new((query.get_cmd(), query.get_query())))); + let _ = tx_reader.send((EvReaderRestart, Box::new((query.get_cmd(), query.get_query(), false)))); + // send redraw event + redraw_query(query); + }; + + let on_query_force_update = |query: &query::Query| { + // restart the reader with new parameter + let _ = tx_reader.send((EvReaderRestart, Box::new((query.get_cmd(), query.get_query(), true)))); // send redraw event redraw_query(query); }; @@ -326,11 +333,17 @@ fn real_main() -> i32 { on_query_change(&query); } - EvActRotateMode => { - query.act_query_rotate_mode(); + EvActToggleInteractive => { + query.act_query_toggle_interactive(); redraw_query(&query); } + EvActRotateMode => { + // tell the matcher to switch mode + let _ = tx_item.send((EvActRotateMode, Box::new(false))); + on_query_force_update(&query); + } + EvActAccept => { // sync with model to quit diff --git a/src/matcher.rs b/src/matcher.rs index 2dd3e998..ef8b0e5c 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -36,7 +36,6 @@ enum Algorithm { #[derive(Clone, Copy, PartialEq)] enum MatcherMode { Regex, - Plain, Fuzzy, Exact, } @@ -104,6 +103,7 @@ impl Matcher { let mut matcher_engine: Option> = None; let mut num_processed: usize = 0; + let mut matcher_mode = self.mode; loop { if matcher_restart.load(Ordering::Relaxed) { @@ -147,7 +147,31 @@ impl Matcher { // notifiy the model that the query had been changed let _ = self.tx_result.send((Event::EvModelRestart, Box::new(true))); - matcher_engine = Some(EngineFactory::build(&query, self.mode)); + let mode_string = match matcher_mode { + MatcherMode::Regex => "RE".to_string(), + MatcherMode::Exact => "EX".to_string(), + _ => "".to_string(), + }; + let _ = self.tx_result.send((Event::EvModelNotifyMatcherMode, Box::new(mode_string))); + + matcher_engine = Some(EngineFactory::build(&query, matcher_mode)); + } + + Event::EvActRotateMode => { + if self.mode == MatcherMode::Regex { + // sk started with regex mode. + matcher_mode = if matcher_mode == self.mode { + MatcherMode::Fuzzy + } else { + MatcherMode::Regex + }; + } else { + matcher_mode = if matcher_mode == self.mode { + MatcherMode::Regex + } else { + self.mode + } + } } _ => {} @@ -542,7 +566,6 @@ impl EngineFactory { pub fn build(query: &str, mode: MatcherMode) -> Box { match mode { MatcherMode::Regex => Box::new(RegexEngine::builder(query).build()), - MatcherMode::Plain => Box::new(FuzzyEngine::builder(query).build()), MatcherMode::Fuzzy | MatcherMode::Exact => { if query.contains(" ") { Box::new(AndEngine::builder(query, mode).build()) @@ -615,9 +638,6 @@ mod test { let x1 = EngineFactory::build("'abc | def ^gh ij | kl mn", MatcherMode::Fuzzy); assert_eq!(x1.display(), "(And: (Or: (Exact: abc), (Fuzzy: def)), (PrefixExact: gh), (Or: (Fuzzy: ij), (Fuzzy: kl)), (Fuzzy: mn))"); - let x2 = EngineFactory::build("'abc | def ^gh ij | kl mn", MatcherMode::Plain); - assert_eq!(x2.display(), "(Fuzzy: 'abc | def ^gh ij | kl mn)"); - let x3 = EngineFactory::build("'abc | def ^gh ij | kl mn", MatcherMode::Regex); assert_eq!(x3.display(), "(Regex: 'abc | def ^gh ij | kl mn)"); diff --git a/src/model.rs b/src/model.rs index 4fe3304d..6e1ec392 100644 --- a/src/model.rs +++ b/src/model.rs @@ -44,6 +44,7 @@ pub struct Model { matcher_stopped: bool, num_read: usize, num_processed: usize, + matcher_mode: String, timer: Instant, } @@ -69,6 +70,7 @@ impl Model { reader_stopped: false, matcher_stopped: false, timer: Instant::now(), + matcher_mode: "".to_string(), theme: ColorTheme::new(), } } @@ -142,6 +144,10 @@ impl Model { } + Event::EvModelNotifyMatcherMode => { + self.matcher_mode = *arg.downcast().unwrap(); + } + Event::EvMatcherStopped => { self.matcher_stopped = true; self.act_redraw_items_and_status(&curses); @@ -332,6 +338,11 @@ impl Model { // display matched/total number curses.cprint(format!(" {}/{}", self.items.len(), self.num_read).as_ref(), COLOR_INFO, false); + // display the matcher mode + if !self.matcher_mode.is_empty() { + curses.cprint(format!("/{}", &self.matcher_mode).as_ref(), COLOR_INFO, false); + } + // display the percentage of the number of processed items if self.num_processed < self.num_read { curses.cprint(format!(" ({}%) ", self.num_processed*100 / self.num_read).as_ref(), COLOR_INFO, false) diff --git a/src/query.rs b/src/query.rs index d82b236f..a24c4077 100644 --- a/src/query.rs +++ b/src/query.rs @@ -138,7 +138,7 @@ impl Query { //------------------------------------------------------------------------------ // Actions // - pub fn act_query_rotate_mode(&mut self) { + pub fn act_query_toggle_interactive(&mut self) { self.mode = match self.mode { QueryMode::QUERY => QueryMode::CMD, QueryMode::CMD => QueryMode::QUERY, diff --git a/src/reader.rs b/src/reader.rs index 09624a71..15df126d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -113,8 +113,8 @@ impl Reader { match ev { Event::EvReaderRestart => { // close existing command or file if exists - let (cmd, query) = *arg.downcast::<(String, String)>().unwrap(); - if cmd == last_command && query == last_query { continue; } + let (cmd, query, force_update) = *arg.downcast::<(String, String, bool)>().unwrap(); + if !force_update && cmd == last_command && query == last_query { continue; } // restart command with new `command` if cmd != last_command {