fix #40, #48 support toggling regex mode dynamically.

1. Change the default key binding for toggleing interactive mode to Ctrl-Q
2. Support toggling regex mode by Ctrl-R (rotate mode)
3. Update README
This commit is contained in:
Mark Wallace 2017-01-19 21:32:46 +08:00
parent 0507a027d9
commit 4f8e6eaabe
8 changed files with 91 additions and 20 deletions

View file

@ -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 <replstr>` 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.

View file

@ -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<Event> {
"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),

View file

@ -488,6 +488,7 @@ fn get_default_key_map() -> HashMap<Key, (Event, Option<String>)> {
//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));

View file

@ -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

View file

@ -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<Box<MatchEngine>> = 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<MatchEngine> {
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)");

View file

@ -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)

View file

@ -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,

View file

@ -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 {