mirror of
https://github.com/lotabout/skim.git
synced 2026-09-10 07:16:23 -04:00
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:
parent
0507a027d9
commit
4f8e6eaabe
35
README.md
35
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 "{}"'
|
||||
```
|
||||
|
||||

|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
23
src/main.rs
23
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
|
||||
|
|
|
|||
11
src/model.rs
11
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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue