Compare commits

...

161 Commits
0.4.0 ... 0.8.3

Author SHA1 Message Date
Junegunn Choi
d82e38adc1 0.8.3 2014-04-05 12:56:10 +09:00
Junegunn Choi
af677e7e35 Vim plugin: do not enable tmux-integration if version < 1.7 2014-04-04 12:43:29 +09:00
Junegunn Choi
6ad38bdad3 Update example: suppress error message from fc on bash (#37)
`'fc' -l 1` generated an error message on bash
2014-04-04 10:26:38 +09:00
Junegunn Choi
8b80136a87 Merge pull request #37 from wellle/zsh-history
Feed all zsh history into fzf
2014-04-03 23:14:28 +09:00
Christian Wellenbrock
97de919152 Feed all zsh history into fzf 2014-04-03 16:11:51 +02:00
Junegunn Choi
0eafa725b9 Fix test code indentation 2014-04-03 14:53:47 +09:00
Junegunn Choi
fa212efe5f Fix ranking when multiple regions overlap
e.g.
  Match region #1: [-----------]
  Match region #2:       [---]
  Match region #3:         [------]
2014-04-03 14:51:01 +09:00
Junegunn Choi
a9056ce90c Add gif showing tmux integration 2014-04-03 01:29:21 +09:00
Junegunn Choi
16682a3f92 Update fe example as the exit status from -0 has changed (#36) 2014-04-03 01:20:22 +09:00
Junegunn Choi
02c01c81a0 Improve -0 and -1 as suggested in #36
- Make -0 and -1 work without -q
- Change exit status to 0 when exiting with -0
2014-04-03 01:06:40 +09:00
Junegunn Choi
22d3929ae3 Implement --select-1 and --exit-0 (#27, #36) 2014-04-02 21:41:57 +09:00
Junegunn Choi
ab9fbf1967 Allow --nth option to take multiple indexes (comma-separated) 2014-04-02 01:49:07 +09:00
Junegunn Choi
608ec2b806 set -o nonomatch for zsh (#34)
Avoid error message in an empty directory
2014-04-01 21:39:40 +09:00
Junegunn Choi
e5ae4f0ef6 Do not load interactive parts when not required (#34) 2014-04-01 20:55:26 +09:00
Junegunn Choi
67ba87d390 Avoid CTRL-T error when default shell != zsh (#34) 2014-04-01 20:49:54 +09:00
Junegunn Choi
77d45cb173 Avoid starting interactive bash (#34) 2014-04-01 20:48:15 +09:00
Junegunn Choi
d83febea46 Merge pull request #35 from junegunn/fix-tmux-on-linux
Fix #34: tmux integration on Linux
2014-04-01 17:58:19 +09:00
Junegunn Choi
546a315884 Fix #34: tmux integration on Linux 2014-04-01 08:55:16 +00:00
Junegunn Choi
af616457e3 Use -p option of split-window instead of manual calculation 2014-03-31 13:48:53 +09:00
Junegunn Choi
1a100a2919 No need for screenrow() 2014-03-31 13:36:58 +09:00
Junegunn Choi
a85bb93b69 Fix use of screenrow when tmux height is given in % 2014-03-31 13:22:52 +09:00
Junegunn Choi
057eda060c Installation on other shells 2014-03-31 10:15:56 +09:00
Junegunn Choi
48f9ee6763 Update install script 2014-03-31 01:01:23 +09:00
Junegunn Choi
52b74abb99 Merge pull request #32 from junegunn/nth
Add --nth and --delimiter option
2014-03-30 15:19:05 +09:00
Junegunn Choi
ec4b8a59fa Implement --nth and --delimiter option 2014-03-30 15:12:04 +09:00
Junegunn Choi
cf8dbf8047 Allow setting tmux split height in % 2014-03-28 17:15:45 +09:00
Junegunn Choi
995d380200 Merge pull request #30 from junegunn/keybinding-tmux-split
Make CTRL-T use tmux split when possible
2014-03-28 15:32:23 +09:00
Junegunn Choi
ae86cdf09a Make CTRL-T use tmux split when possible 2014-03-28 15:28:10 +09:00
Junegunn Choi
2b346659a0 Vim plugin: tmux integration 2014-03-28 00:58:07 +09:00
Junegunn Choi
49081711a9 Execute clear before fzf 2014-03-26 01:34:59 +09:00
Junegunn Choi
e7439ce193 Major update to Vim plugin 2014-03-25 19:55:52 +09:00
Junegunn Choi
b8e438b6be Prefer pre-existing function/alias in Vim plugin 2014-03-25 12:05:57 +09:00
Junegunn Choi
678e950b6d Use --reverse option in fco example (#29) 2014-03-20 10:38:53 +09:00
Junegunn Choi
9ea651f1cd Merge pull request #29 from wellle/fix/fco
Fix small typo in Readme
2014-03-20 10:33:47 +09:00
Christian Wellenbrock
bd98a08b89 Fix small typo in Readme 2014-03-20 00:07:47 +01:00
Junegunn Choi
f02bb4fdac Add fe command to examples section as suggested in #27 2014-03-20 01:57:57 +09:00
Junegunn Choi
0a8352a5cd Quote $1 in vimf example (#26) 2014-03-19 21:57:03 +09:00
Junegunn Choi
737423995d Merge pull request #28 from wellle/ignore-dsstore
Add .DS_Store to .gitignore
2014-03-19 21:19:28 +09:00
Christian Wellenbrock
2916bf7ee4 Add .DS_Store to .gitignore 2014-03-19 13:14:20 +01:00
Junegunn Choi
fa54c5d9b0 Merge pull request #26 from wellle/vimf-query
Add --query parameter to fzf invocation in vimf function
2014-03-19 21:09:52 +09:00
Christian Wellenbrock
693b6651b4 Add --query parameter to fzf invocation in vimf function 2014-03-19 12:30:42 +01:00
Junegunn Choi
5c71ecb267 Implement C-Y (yank) 2014-03-15 16:55:20 +09:00
Junegunn Choi
1ba50eba98 Fix gemspec
Reference:
16ead977fa
2014-03-14 18:25:55 +09:00
Junegunn Choi
2c8a256b13 Update README and install
- Unset multi-select option with +m
2014-03-14 17:53:23 +09:00
Junegunn Choi
f4c5aa03d7 Update README and install script
- Added examples: fbr and fco
- Always use local variables
2014-03-14 17:46:55 +09:00
Junegunn Choi
c6acb2a639 Update README 2014-03-13 15:28:01 +09:00
Junegunn Choi
2296013174 Add ALT-C keybinding for bash 2014-03-13 14:29:27 +09:00
Junegunn Choi
8a3e8c2d81 Install curses gem in user's home directory 2014-03-13 11:01:35 +09:00
Junegunn Choi
ae84d8c7a4 Update README 2014-03-09 11:52:35 +09:00
Junegunn Choi
dbd627c38a Update README: Remove section on --disable-gems
This is automatically set in install script. It may only cause unnecessary
confusion.
2014-03-09 10:46:34 +09:00
Junegunn Choi
d172c3ce03 Update README 2014-03-09 10:43:59 +09:00
Junegunn Choi
9904f5354e Add --black for terminals incapable of use_default_colors
See the discussion in #18.

Use --black option to use black background regardless of the default
background color of the terminal. Also, this option can be used to fix
rendering issues on terminals that don't support use_default_colors (man
3 default_colors). Depending on the terminal, use_default_colors may or
may not succeed, but the Ruby version of it always returns nil, it's
currently not possible to automatically enable this option.
2014-03-09 04:09:09 +09:00
Junegunn Choi
f345bf7983 Shift-left/right on OSX 2014-03-08 01:55:48 +09:00
Junegunn Choi
875f9b6534 Reduce timeout to 0.1 sec 2014-03-08 01:55:11 +09:00
Junegunn Choi
871dfb709d Introduce escape time-out for better handling of escape sequences 2014-03-08 01:28:32 +09:00
Junegunn Choi
19e24bd644 Home/End/PgUp/PgDn/Del/(Ins) 2014-03-08 01:02:32 +09:00
Junegunn Choi
457a240457 Add option to disable 256-color output (related #18) 2014-03-07 17:34:11 +09:00
Junegunn Choi
bbf4567dd8 Allow command/control-click/wheel
e.g. urxvt
2014-03-07 17:30:44 +09:00
Junegunn Choi
27d3b52843 Update gem version 2014-03-07 01:37:33 +09:00
Junegunn Choi
dcb4694ec1 Reimplement mouse input without using Curses.getch 2014-03-06 20:52:46 +09:00
Junegunn Choi
2fb8ae010f Completely remove mouse support
Since the version 0.7.0, fzf internally used Curses.getch() call to take user
input, which allowed it to support mouse input as well. However it has turned
out that Curses.getch() has introduced glitches that cannot be easily handled
(e.g. Try resize the terminal). So I finally decided that it's not worth the
trouble and drop the mouse support.
2014-03-06 12:21:09 +09:00
Junegunn Choi
65ae6cabb5 Rename variables 2014-03-05 22:41:45 +09:00
Junegunn Choi
86a66da04d Synchronize getch calls to reduce screen glitches 2014-03-05 19:07:59 +09:00
Junegunn Choi
d66b02b0cd Disable typeahead optimization in Ruby 1.8 2014-03-05 18:00:20 +09:00
Junegunn Choi
b3182c3304 Performance optimization: batch application of input chars 2014-03-05 11:21:20 +09:00
Junegunn Choi
2dbca00bfb Implement --extended-exact option (#24) 2014-03-04 21:29:45 +09:00
Junegunn Choi
b22fd6de6d Fix #22. Keybindings for vi-mode bash. 2014-03-04 18:53:29 +09:00
Junegunn Choi
245ee42763 Update installation instruction 2014-03-04 11:25:50 +09:00
Junegunn Choi
98bef4600c Merge pull request #20 from wellle/zsh-history-fc
Use `fc` instead of `history` to avoid `oh-my-zsh` alias
2014-02-26 19:03:01 +09:00
Christian Wellenbrock
f5d53b94fe Use fc instead of history to avoid omz alias 2014-02-26 10:56:44 +01:00
Junegunn Choi
00c8a68430 Unalias history on zsh (related #19) 2014-02-26 11:46:30 +09:00
Junegunn Choi
c1be834ff9 Merge pull request #19 from wellle/zsh_history
Feed all zsh history into fzf (not only most recent)
2014-02-25 23:41:54 +09:00
Christian Wellenbrock
2c0dc2f3b1 Feed all zsh history into fzf (not only most recent) 2014-02-25 15:40:52 +01:00
Junegunn Choi
1c94fef720 Update version number 2014-02-20 15:17:29 +09:00
Junegunn Choi
b711d76b8e Choose to use 256-colors when $TERM includes 256 (related: #18)
It turned out that Curses.can_change_color? returns false when $TERM is
set to screen-256color, which is perfectly capable of rendering 256
colors.
2014-02-20 13:38:04 +09:00
Junegunn Choi
4396ab7548 Do not set key bindings in non-interactive shell 2014-02-15 01:29:16 +09:00
Junegunn Choi
2b8c2b9f2a CTRL-R for bash: Unset $HISTTIMEFORMAT 2014-02-13 16:47:53 +09:00
Junegunn Choi
426284c87e Change CTRL-T binding to include directories 2014-02-07 18:41:05 +09:00
Junegunn Choi
089691faaf Cache the result as sorted 2014-02-02 21:41:08 +09:00
Junegunn Choi
301290663d Add -f (--filter) option (#15)
This commit adds --filter option so that fzf can be used as a simple unix
filter instead of being an interactive fuzzy finder.
2014-02-02 01:45:44 +09:00
Junegunn Choi
1155da7e1c Install curses 1.0.0 2014-02-02 01:42:04 +09:00
Junegunn Choi
eca0a99fb4 Proper handling of typeahead arrow keys
To reproduce: `sleep 2; fzf` and press arrow keys
2014-02-01 10:07:59 +09:00
Junegunn Choi
96215c4619 CTRL-L to clear and redraw the screen 2014-02-01 02:05:58 +09:00
Junegunn Choi
b2d2be55ef init_screen must be called within render block 2014-01-31 15:56:37 +09:00
Junegunn Choi
7280e8ebc2 Merge pull request #17 from junegunn/mouse
Add mouse support
2014-01-30 07:37:01 -08:00
Junegunn Choi
c7e86ad4f1 Add --no-mouse option to replace FZF_MOUSE_ENABLED 2014-01-30 15:41:44 +09:00
Junegunn Choi
f2b2c022be Update gem version 2014-01-30 14:47:51 +09:00
Junegunn Choi
7747daa9ec Merge branch 'master' into mouse 2014-01-30 03:14:13 +09:00
Junegunn Choi
c2943e7681 Fix incompatible encoding regexp match from width call 2014-01-30 03:12:12 +09:00
Junegunn Choi
d5fc03d867 Update README 2014-01-30 02:51:30 +09:00
Junegunn Choi
b0eca20dc2 Minor refactoring 2014-01-30 02:51:06 +09:00
Junegunn Choi
aad335475c Shift-click and wheel 2014-01-30 01:01:31 +09:00
Junegunn Choi
c3676bf986 Make install script prefer system ruby 2014-01-29 11:04:07 +09:00
Junegunn Choi
6fb4b6d097 Do not move vcursor on select using mouse 2014-01-29 02:10:08 +09:00
Junegunn Choi
6aa168833b Ruby 1.8 compatibility 2014-01-29 02:08:18 +09:00
Junegunn Choi
0d83cae2ec Implement mouse support 2014-01-28 19:02:55 +09:00
Junegunn Choi
773d9976a0 Use Curses.getch to support mouse (WIP) 2014-01-28 02:58:20 +09:00
Junegunn Choi
3723829b0a Add FZF_DEFAULT_OPTS and update command-line options 2014-01-22 12:03:17 +09:00
Junegunn Choi
13cb198b5c Update README 2014-01-14 16:51:52 +09:00
Junegunn Choi
79f645aa6c Update README 2014-01-07 17:07:02 +09:00
Junegunn Choi
42d479d071 --version 2013-12-28 02:25:24 +09:00
Junegunn Choi
d7f50b1e41 Fix typo in install script 2013-12-26 01:54:29 +09:00
Junegunn Choi
39eb85596c Fix error on Rubinius 2013-12-26 01:43:20 +09:00
Junegunn Choi
bff7e9edf5 Should not --disable-gems when curses gem is used (#14) 2013-12-26 01:39:17 +09:00
Junegunn Choi
98ccc03a21 Update README.md 2013-12-26 01:15:46 +09:00
Junegunn Choi
3b668ed448 Install curses gem when not found (#14) 2013-12-26 01:06:46 +09:00
Junegunn Choi
33b28be941 Make host name completion require trigger sequence (#13) 2013-12-23 23:16:07 +09:00
Junegunn Choi
76fe23b928 Fix host completion to include ssh_config entries (#13) 2013-12-22 20:45:35 +09:00
Junegunn Choi
622c54f4a3 Update gem version (0.6.0)
- Smart-case pattern matching
- CTRL-Q
2013-12-22 16:00:06 +09:00
Junegunn Choi
e09993f919 Update README 2013-12-22 00:36:39 +09:00
Junegunn Choi
7ee6fd1f6d Make install script to add key bindings as well 2013-12-22 00:18:41 +09:00
Junegunn Choi
2dca6f0cb2 Update Last update 2013-12-20 16:13:38 +09:00
Junegunn Choi
159dd7f069 Implement smart-case match (#12) 2013-12-20 15:30:48 +09:00
Junegunn Choi
b30f21e074 CTRL-Q to terminate the finder (#11) 2013-12-20 14:01:28 +09:00
Junegunn Choi
636c86cf6f Update bash host completion for ssh and telnet commands 2013-12-20 11:18:28 +09:00
Junegunn Choi
5483e41b2a Update README 2013-12-14 22:30:09 +09:00
Junegunn Choi
1c89994c94 Suppress warnings on old version of Ruby 2013-12-11 01:03:47 +09:00
Junegunn Choi
e1bc4b983e Update gem version 2013-12-11 01:00:11 +09:00
Junegunn Choi
cb3645ea95 Fix ^.*$ pattern matching in extended-search mode (#9) 2013-12-09 14:46:06 +09:00
Junegunn Choi
04ebaddf5e 0.5.1 2013-12-08 02:09:50 +09:00
Junegunn Choi
45e1f1ae57 Last update: December 5, 2013 2013-12-05 13:29:52 +09:00
Junegunn Choi
c1d5f7cef7 Do not use 256-color if not supported (#8) 2013-12-05 12:17:21 +09:00
Junegunn Choi
df663c4e41 Improve bash completion
- kill completion: do not even start fzf on non-empty word
- host completion: start fzf with initial query
2013-11-29 23:42:00 +09:00
Junegunn Choi
d3742782f3 Fix a typo in README 2013-11-29 18:09:51 +09:00
Junegunn Choi
faff17b2a9 Hostname completion for ssh and telnet commands 2013-11-29 18:08:22 +09:00
Junegunn Choi
9a3cddc92e Apply FZF_COMPLETION_OPTS to kill completion 2013-11-29 17:53:30 +09:00
Junegunn Choi
bd2763d863 Add bash completion for kill command 2013-11-29 17:50:53 +09:00
Junegunn Choi
b2bb22d883 A minor update to install script 2013-11-29 13:42:13 +09:00
Junegunn Choi
ad8ec7f387 Encourage use of function instead of alias (exportability) 2013-11-29 13:40:31 +09:00
Junegunn Choi
cf0ca8578c Update bash key binding example 2013-11-27 10:20:27 +09:00
Junegunn Choi
07aee79bd8 Update examples and bash completion
- Use tput sc/rc instead of redraw-current-line
- Escape selected items with printf
2013-11-27 01:14:36 +09:00
Junegunn Choi
344b57fe33 grep -F 2013-11-26 19:05:20 +09:00
Junegunn Choi
18a2fbf54a Fix install script (use export-able function instead of alias) 2013-11-26 19:01:01 +09:00
Junegunn Choi
39af56cf8f Revert "Reduce the number of Curses.refresh calls"
This reverts commit 2d3a0a1034
(which doesn't make any noticeable difference)
2013-11-24 20:40:23 +09:00
Junegunn Choi
2d3a0a1034 Reduce the number of Curses.refresh calls 2013-11-24 13:40:02 +09:00
Junegunn Choi
655fa5d9aa Update query line after update_list call
This commit is the workaround for the curses issue where the query
string on the screen is truncated after the cursor when the list is
updated: e.g. `aaac|bbb`
2013-11-24 12:56:26 +09:00
Junegunn Choi
9a49a29c7f Fix bash completion (~/abc/def/ghi**)
~/abc/def/ghi** should match ghi under ~/abc/def/, not ~/abc/def*
2013-11-23 20:37:53 +09:00
Junegunn Choi
89ae45cda4 Merge branch 'master' of github.com:junegunn/fzf
Conflicts:
	fzf-completion.bash
2013-11-23 20:16:46 +09:00
Junegunn Choi
f660ad35b2 Improve bash completion: [DIRECTORY/][FUZZY_PATTERN]**<TAB> 2013-11-23 20:12:14 +09:00
Junegunn Choi
c61738ae43 Bump up gem version 2013-11-23 20:09:02 +09:00
Junegunn Choi
c4dec4d34b Add -q option (initial query) 2013-11-23 19:21:02 +09:00
Junegunn Choi
a797604255 -o default as well as -o bashdefault 2013-11-21 11:38:14 +09:00
Junegunn Choi
25840d3bc7 -o bashdefault instead of -o default 2013-11-21 10:49:23 +09:00
Junegunn Choi
4745d50931 Add CTRL-G and ESC (C-[) as abort key (#7) 2013-11-20 21:18:51 +09:00
Junegunn Choi
04bf3abe99 Fix bash completion example 2013-11-20 15:22:25 +09:00
Junegunn Choi
57f7963eee Remove obsolete lines 2013-11-20 14:02:29 +09:00
Junegunn Choi
2fa21e5dd6 Remove obsolete lines 2013-11-20 14:01:13 +09:00
Junegunn Choi
9c4c37aa36 Adjust completion types (all/file/dir) 2013-11-20 12:28:41 +09:00
Junegunn Choi
2540c9062f The last argument doesn't have to be a path 2013-11-20 10:46:53 +09:00
Junegunn Choi
f28274109f Update Vim plugin to take path argument 2013-11-20 10:31:33 +09:00
Junegunn Choi
724724bd8c Extend the list of commands for fzf-completion 2013-11-20 02:23:30 +09:00
Junegunn Choi
64541cb5f8 Fix install script (source ~/.xxxrc has no effect) 2013-11-20 02:10:19 +09:00
Junegunn Choi
179b00ed6c Reload .bashrc/.zshrc after installation 2013-11-20 01:57:24 +09:00
Junegunn Choi
a9fd496691 Merge pull request #6 from junegunn/completion
Prototype implementation of bash auto-completion
2013-11-19 08:44:30 -08:00
Junegunn Choi
b14c57e656 Update README 2013-11-20 01:42:57 +09:00
Junegunn Choi
fa5617e076 Implement bash auto-completion with fzf 2013-11-20 01:29:36 +09:00
Junegunn Choi
e52a1d5fad Update bash example 2013-11-18 17:36:54 +09:00
Junegunn Choi
423e26b0c9 Better handling of NFD chars 2013-11-17 12:32:38 +09:00
Junegunn Choi
84921df0e3 Fix extended-search on non-darwin env 2013-11-17 11:47:52 +09:00
Junegunn Choi
6a5e1de6f3 Fix missing NFD conversion in extended-search mode 2013-11-17 11:20:06 +09:00
Junegunn Choi
90adda73b0 Update Vim plugin
Changes:
- Rename g:fzf_command to g:fzf_source
- Support multi-select mode
- Add fzf#run(vim_command, fzf_args) function

Todo:
- Faster startup with --disable-gems option when available
2013-11-17 02:41:10 +09:00
11 changed files with 1953 additions and 350 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
pkg
Gemfile.lock
.DS_Store

426
README.md
View File

@@ -5,6 +5,8 @@ fzf is a general-purpose fuzzy finder for your shell.
![](https://raw.github.com/junegunn/i/master/fzf.gif)
([tmux integration!](https://cloud.githubusercontent.com/assets/700826/2593609/3ec13962-ba83-11e3-88d3-f9f95bd8a64b.gif))
It was heavily inspired by [ctrlp.vim](https://github.com/kien/ctrlp.vim) and
the likes.
@@ -16,54 +18,33 @@ fzf requires Ruby (>= 1.8.5).
Installation
------------
Download [fzf executable](https://raw.github.com/junegunn/fzf/master/fzf) and
put it somewhere in your search $PATH.
```sh
mkdir -p ~/bin
wget https://raw.github.com/junegunn/fzf/master/fzf -O ~/bin/fzf
chmod +x ~/bin/fzf
```
Or you can just clone this repository and run
Clone this repository and run
[install](https://github.com/junegunn/fzf/blob/master/install) script.
```sh
git clone https://github.com/junegunn/fzf.git
fzf/install
git clone https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```
Make sure that ~/bin is included in $PATH.
The script will setup:
```sh
export PATH=$PATH:~/bin
```
- `fzf` executable
- Key bindings (`CTRL-T`, `CTRL-R`, and `ALT-C`) for bash and zsh
- Fuzzy auto-completion for bash
### Install as Ruby gem
fzf can be installed as a Ruby gem
```
gem install fzf
```
It's a bit easier to install and update the script but the Ruby gem version
takes slightly longer to start.
If you don't use bash or zsh, you have to manually place fzf executable in a
directory included in `$PATH`. Key bindings are not yet supported.
### Install as Vim plugin
You can use any Vim plugin manager to install fzf for Vim. If you don't use one,
I recommend you try [vim-plug](https://github.com/junegunn/vim-plug).
Once you have cloned the repository, add the following line to your .vimrc.
1. [Install vim-plug](https://github.com/junegunn/vim-plug#usage)
2. Edit your .vimrc
```vim
set rtp+=~/.fzf
```
call plug#begin()
Plug 'junegunn/fzf'
" ...
call plug#end()
3. Run `:PlugInstall`
Or you may use any Vim plugin manager, such as
[vim-plug](https://github.com/junegunn/vim-plug).
Usage
-----
@@ -71,12 +52,35 @@ Usage
```
usage: fzf [options]
-m, --multi Enable multi-select
-x, --extended Extended-search mode
-s, --sort=MAX Maximum number of matched items to sort. Default: 1000
+s, --no-sort Do not sort the result. Keep the sequence unchanged.
+i Case-sensitive match
+c, --no-color Disable colors
Search
-x, --extended Extended-search mode
-e, --extended-exact Extended-search mode (exact match)
-i Case-insensitive match (default: smart-case match)
+i Case-sensitive match
-n, --nth=[-]N[,..] Comma-separated list of field indexes for limiting
search scope (positive or negative integers)
-d, --delimiter=STR Field delimiter regex for --nth (default: AWK-style)
Search result
-s, --sort=MAX Maximum number of matched items to sort (default: 1000)
+s, --no-sort Do not sort the result. Keep the sequence unchanged.
Interface
-m, --multi Enable multi-select with tab/shift-tab
--no-mouse Disable mouse
+c, --no-color Disable colors
+2, --no-256 Disable 256-color
--black Use black background
Scripting
-q, --query=STR Start the finder with the given query
-1, --select-1 Automatically select the only match
-0, --exit-0 Exit immediately when there's no match
-f, --filter=STR Filter mode. Do not start interactive finder.
Environment variables
FZF_DEFAULT_COMMAND Default command to use when input is tty
FZF_DEFAULT_OPTS Defaults options. (e.g. "-x -m --sort 10000")
```
fzf will launch curses-based finder, read the list from STDIN, and write the
@@ -101,21 +105,25 @@ If you want to preserve the exact sequence of the input, provide `--no-sort` (or
history | fzf +s
```
### Key binding
### Keys
Use CTRL-J and CTRL-K (or CTRL-N and CTRL-P) to change the selection, press
enter key to select the item. CTRL-C will terminate the finder.
enter key to select the item. CTRL-C, CTRL-G, or ESC will terminate the finder.
The following readline key bindings should also work as expected.
- CTRL-A / CTRL-E
- CTRL-B / CTRL-F
- CTRL-W / CTRL-U
- CTRL-W / CTRL-U / CTRL-Y
- ALT-B / ALT-F
If you enable multi-select mode with `-m` option, you can select multiple items
with TAB or Shift-TAB key.
You can also use mouse. Double-click on an item to select it or shift-click (or
ctrl-click) to select multiple items. Use mouse wheel to move the cursor up and
down.
### Extended-search mode
With `-x` or `--extended` option, fzf will start in "extended-search mode".
@@ -132,48 +140,39 @@ such as: `^music .mp3$ sbtrkt !rmx`
| `'wild` | Items that include `wild` | exact-match (quoted) |
| `!'fire` | Items that do not include `fire` | inverse-exact-match |
Usage as Vim plugin
-------------------
If you don't need fuzzy matching and do not wish to "quote" every word, start
fzf with `-e` or `--extended-exact` option.
If you install fzf as a Vim plugin, `:FZF` command will be added.
```vim
:FZF
:FZF --no-sort
```
You can override the command which produces input to fzf.
```vim
let g:fzf_command = 'find . -type f'
```
Most of the time, you will prefer native Vim plugins with better integration
with Vim. The only reason one might consider using fzf in Vim is its speed. For
a very large list of files, fzf is significantly faster and it does not block.
Useful bash examples
--------------------
Useful examples
---------------
```sh
# vimf - Open selected file in Vim
vimf() {
FILE=$(fzf) && vim "$FILE"
# fe [FUZZY PATTERN] - Open the selected file with the default editor
# - Bypass fuzzy finder if there's only one match (--select-1)
# - Exit if there's no match (--exit-0)
fe() {
local file
file=$(fzf --query="$1" --select-1 --exit-0)
[ -n "$file" ] && ${EDITOR:-vim} "$file"
}
# fd - cd to selected directory
fd() {
DIR=$(find ${1:-*} -path '*/\.*' -prune -o -type d -print 2> /dev/null | fzf) && cd "$DIR"
local dir
dir=$(find ${1:-*} -path '*/\.*' -prune \
-o -type d -print 2> /dev/null | fzf +m) &&
cd "$dir"
}
# fda - including hidden directories
fda() {
DIR=$(find ${1:-*} -type d 2> /dev/null | fzf) && cd "$DIR"
local dir
dir=$(find ${1:-.} -type d 2> /dev/null | fzf +m) && cd "$dir"
}
# fh - repeat history
fh() {
eval $(history | fzf +s | sed 's/ *[0-9]* *//')
eval $(([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s | sed 's/ *[0-9]* *//')
}
# fkill - kill process
@@ -181,81 +180,254 @@ fkill() {
ps -ef | sed 1d | fzf -m | awk '{print $2}' | xargs kill -${1:-9}
}
# (Assuming you don't use the default CTRL-T and CTRL-R)
# fbr - checkout git branch
fbr() {
local branches branch
branches=$(git branch) &&
branch=$(echo "$branches" | fzf +s +m) &&
git checkout $(echo "$branch" | sed "s/.* //")
}
# CTRL-T - Paste the selected file path into the command line
bind '"\er": redraw-current-line'
bind '"\C-t": " \C-u \C-a\C-k$(fzf)\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er"'
# fco - checkout git commit
fco() {
local commits commit
commits=$(git log --pretty=oneline --abbrev-commit --reverse) &&
commit=$(echo "$commits" | fzf +s +m -e) &&
git checkout $(echo "$commit" | sed "s/ .*//")
}
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": " \C-e\C-u$(history | fzf +s | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
# ftags - search ctags
ftags() {
local line
[ -e tags ] &&
line=$(
awk 'BEGIN { FS="\t" } !/^!/ {print toupper($4)"\t"$1"\t"$2"\t"$3}' tags |
cut -c1-80 | fzf --nth=1,2
) && $EDITOR $(cut -f3 <<< "$line") -c "set nocst" \
-c "silent tag $(cut -f2 <<< "$line")"
}
```
zsh widgets
-----------
Key bindings for command line
-----------------------------
The install script will setup the following key bindings.
### bash/zsh
- `CTRL-T` - Paste the selected file path(s) into the command line
- `CTRL-R` - Paste the selected command from history into the command line
- `ALT-C` - cd into the selected directory
If you're on a tmux session, `CTRL-T` will launch fzf in a new split-window. You
may disable this tmux integration by setting `FZF_TMUX` to 0, or change the
height of the window with `FZF_TMUX_HEIGHT` (e.g. `20`, `50%`).
The source code can be found in `~/.fzf.bash` and in `~/.fzf.zsh`.
Auto-completion
---------------
Disclaimer: *Auto-completion feature is currently experimental, it can change
over time*
### bash
#### Files and directories
Fuzzy completion for files and directories can be triggered if the word before
the cursor ends with the trigger sequence which is by default `**`.
- `COMMAND [DIRECTORY/][FUZZY_PATTERN]**<TAB>`
```sh
# CTRL-T - Paste the selected file(s) path into the command line
fzf-file-widget() {
local FILES
local IFS="
"
FILES=($(
find * -path '*/\.*' -prune \
-o -type f -print \
-o -type l -print 2> /dev/null | fzf -m))
unset IFS
FILES=$FILES:q
LBUFFER="${LBUFFER%% #} $FILES"
zle redisplay
}
zle -N fzf-file-widget
bindkey '^T' fzf-file-widget
# Files under current directory
# - You can select multiple items with TAB key
vim **<TAB>
# ALT-C - cd into the selected directory
fzf-cd-widget() {
cd "${$(find * -path '*/\.*' -prune \
-o -type d -print 2> /dev/null | fzf):-.}"
zle reset-prompt
}
zle -N fzf-cd-widget
bindkey '\ec' fzf-cd-widget
# Files under parent directory
vim ../**<TAB>
# CTRL-R - Paste the selected command from history into the command line
fzf-history-widget() {
LBUFFER=$(history | fzf +s | sed "s/ *[0-9]* *//")
zle redisplay
}
zle -N fzf-history-widget
bindkey '^R' fzf-history-widget
# Files under parent directory that match `fzf`
vim ../fzf**<TAB>
# Files under your home directory
vim ~/**<TAB>
# Directories under current directory (single-selection)
cd **<TAB>
# Directories under ~/github that match `fzf`
cd ~/github/fzf**<TAB>
```
#### Process IDs
Fuzzy completion for PIDs is provided for kill command. In this case
there is no trigger sequence, just press tab key after kill command.
```sh
# Can select multiple processes with <TAB> or <Shift-TAB> keys
kill -9 <TAB>
```
#### Host names
For ssh and telnet commands, fuzzy completion for host names is provided. The
names are extracted from /etc/hosts and ~/.ssh/config.
```sh
ssh **<TAB>
telnet **<TAB>
```
#### Settings
```sh
# Use ~~ as the trigger sequence instead of the default **
export FZF_COMPLETION_TRIGGER='~~'
# Options to fzf command
export FZF_COMPLETION_OPTS='+c -x'
```
### zsh
TODO :smiley:
(Pull requests are appreciated.)
Usage as Vim plugin
-------------------
(fzf is a command-line utility, naturally it is only accessible in terminal Vim)
### `:FZF[!]`
If you have set up fzf for Vim, `:FZF` command will be added.
```vim
" Look for files under current directory
:FZF
" Look for files under your home directory
:FZF ~
" With options
:FZF --no-sort -m /tmp
```
Note that the environment variables `FZF_DEFAULT_COMMAND` and `FZF_DEFAULT_OPTS`
also apply here.
If you're on a tmux session, `:FZF` will launch fzf in a new split-window whose
height can be adjusted with `g:fzf_tmux_height` (default: '40%'). However, the
bang version (`:FZF!`) will always start in fullscreen.
### `fzf#run([options])`
For more advanced uses, you can call `fzf#run()` function which returns the list
of the selected items.
`fzf#run()` may take an options-dictionary:
| Option name | Type | Description |
| ----------- | ------------- | ------------------------------------------------------------------- |
| `source` | string | External command to generate input to fzf (e.g. `find .`) |
| `source` | list | Vim list as input to fzf |
| `sink` | string | Vim command to handle the selected item (e.g. `e`, `tabe`) |
| `sink` | funcref | Reference to function to process each selected item |
| `options` | string | Options to fzf |
| `dir` | string | Working directory |
| `tmux` | number/string | Use tmux split if possible with the given height (e.g. `20`, `50%`) |
#### Examples
If `sink` option is not given, `fzf#run` will simply return the list.
```vim
let items = fzf#run({ 'options': '-m +c', 'dir': '~', 'source': 'ls' })
```
But if `sink` is given as a string, the command will be executed for each
selected item.
```vim
" Each selected item will be opened in a new tab
let items = fzf#run({ 'sink': 'tabe', 'options': '-m +c', 'dir': '~', 'source': 'ls' })
```
We can also use a Vim list as the source as follows:
```vim
" Choose a color scheme with fzf
nnoremap <silent> <Leader>C :call fzf#run({
\ 'source':
\ map(split(globpath(&rtp, "colors/*.vim"), "\n"),
\ "substitute(fnamemodify(v:val, ':t'), '\\..\\{-}$', '', '')"),
\ 'sink': 'colo',
\ 'options': '+m',
\ 'tmux': 15
\ })<CR>
```
`sink` option can be a function reference. The following example creates a
handy mapping that selects an open buffer.
```vim
" List of buffers
function! g:buflist()
redir => ls
silent ls
redir END
return split(ls, '\n')
endfunction
function! g:bufopen(e)
execute 'buffer '. matchstr(a:e, '^[ 0-9]*')
endfunction
nnoremap <silent> <Leader><Enter> :call fzf#run({
\ 'source': g:buflist(),
\ 'sink': function('g:bufopen'),
\ 'options': '+m +s',
\ 'tmux': 15
\ })<CR>
```
Tips
----
### Faster startup with `--disable-gems` options
### Rendering issues
If you're running Ruby 1.9 or above, you can improve the startup time with
`--disable-gems` option to Ruby.
If you have any rendering issues, check the followings:
- `time ruby ~/bin/fzf -h`
- 0.077 sec
- `time ruby --disable-gems ~/bin/fzf -h`
- 0.025 sec
1. Make sure `$TERM` is correctly set. fzf will use 256-color only if it
contains `256` (e.g. `xterm-256color`)
2. If you're on screen or tmux, `$TERM` should be either `screen` or
`screen-256color`
3. Some terminal emulators (e.g. mintty) have problem displaying default
background color and make some text unable to read. In that case, try `--black`
option. And if it solves your problem, I recommend including it in
`FZF_DEFAULT_OPTS` for further convenience.
4. If you still have problem, try `--no-256` option or even `--no-color`.
5. Ruby 1.9 or above is required for correctly displaying unicode characters.
Define fzf alias with the option as follows:
### Ranking algorithm
fzf sorts the result first by the length of the matched substring, then by the
length of the whole string. However it only does so when the number of matches
is less than the limit which is by default 1000, in order to avoid the cost of
sorting a large list and limit the response time of the query.
This limit can be adjusted with `-s` option, or with the environment variable
`FZF_DEFAULT_OPTS`.
```sh
alias fzf='ruby --disable-gems ~/bin/fzf'
export FZF_DEFAULT_OPTS="--sort 20000"
```
### Incorrect display on Ruby 1.8
It is reported that the output of fzf can become unreadable on some terminals
when it's running on Ruby 1.8. If you experience the problem, upgrade your Ruby
to 1.9 or above. Ruby 1.9 or above is also required for displaying Unicode
characters.
License
-------

9
ext/mkrf_conf.rb Normal file
View File

@@ -0,0 +1,9 @@
require 'rubygems/dependency_installer'
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.1.0')
Gem::DependencyInstaller.new.install 'curses', '~> 1.0'
end
File.open(File.expand_path('../Rakefile', __FILE__), 'w') do |f|
f.puts 'task :default'
end

768
fzf

File diff suppressed because it is too large Load Diff

167
fzf-completion.bash Normal file
View File

@@ -0,0 +1,167 @@
#!/bin/bash
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/-completion.bash
#
# - $FZF_COMPLETION_TRIGGER (default: '**')
# - $FZF_COMPLETION_OPTS (default: empty)
_fzf_opts_completion() {
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-m --multi -x --extended -s --sort +s +i +c --no-color"
case "${prev}" in
--sort|-s)
COMPREPLY=( $(compgen -W "$(seq 2000 1000 10000)" -- ${cur}) )
return 0
;;
esac
if [[ ${cur} =~ ^-|\+ ]]; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
return 0
}
_fzf_generic_completion() {
local cur base dir leftover matches trigger
COMPREPLY=()
trigger=${FZF_COMPLETION_TRIGGER:-**}
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ ${cur} == *"$trigger" ]]; then
base=${cur:0:${#cur}-${#trigger}}
eval base=$base
dir="$base"
while [ 1 ]; do
if [ -z "$dir" -o -d "$dir" ]; then
leftover=${base/#"$dir"}
leftover=${leftover/#\/}
[ "$dir" = './' ] && dir=''
tput sc
matches=$(find "$dir"* $1 2> /dev/null | fzf $FZF_COMPLETION_OPTS $2 -q "$leftover" | while read item; do
printf '%q ' "$item"
done)
matches=${matches% }
if [ -n "$matches" ]; then
COMPREPLY=( "$matches" )
else
COMPREPLY=( "$cur" )
fi
tput rc
return 0
fi
dir=$(dirname "$dir")
[[ "$dir" =~ /$ ]] || dir="$dir"/
done
fi
}
_fzf_all_completion() {
_fzf_generic_completion \
"-name .git -prune -o -name .svn -prune -o -type d -print -o -type f -print -o -type l -print" \
"-m"
}
_fzf_file_completion() {
_fzf_generic_completion \
"-name .git -prune -o -name .svn -prune -o -type f -print -o -type l -print" \
"-m"
}
_fzf_dir_completion() {
_fzf_generic_completion \
"-name .git -prune -o -name .svn -prune -o -type d -print" \
""
}
_fzf_kill_completion() {
[ -n "${COMP_WORDS[COMP_CWORD]}" ] && return 1
local selected
tput sc
selected=$(ps -ef | sed 1d | fzf -m $FZF_COMPLETION_OPTS | awk '{print $2}' | tr '\n' ' ')
tput rc
if [ -n "$selected" ]; then
COMPREPLY=( "$selected" )
return 0
fi
}
_fzf_telnet_completion() {
local cur selected trigger
trigger=${FZF_COMPLETION_TRIGGER:-**}
cur="${COMP_WORDS[COMP_CWORD]}"
[[ ${cur} == *"$trigger" ]] || return 1
cur=${cur:0:${#cur}-${#trigger}}
tput sc
selected=$(grep -v '^\s*\(#\|$\)' /etc/hosts | awk '{print $2}' | sort -u | fzf $FZF_COMPLETION_OPTS -q "$cur")
tput rc
if [ -n "$selected" ]; then
COMPREPLY=("$selected")
return 0
fi
}
_fzf_ssh_completion() {
local cur selected trigger
trigger=${FZF_COMPLETION_TRIGGER:-**}
cur="${COMP_WORDS[COMP_CWORD]}"
[[ ${cur} == *"$trigger" ]] || return 1
cur=${cur:0:${#cur}-${#trigger}}
tput sc
selected=$(cat \
<(cat ~/.ssh/config /etc/ssh/ssh_config 2> /dev/null | grep -i ^host) \
<(grep -v '^\s*\(#\|$\)' /etc/hosts) | \
awk '{print $2}' | sort -u | fzf $FZF_COMPLETION_OPTS -q "$cur")
tput rc
if [ -n "$selected" ]; then
COMPREPLY=("$selected")
return 0
fi
}
complete -F _fzf_opts_completion fzf
# Directory
for cmd in "cd pushd rmdir"; do
complete -F _fzf_dir_completion -o default -o bashdefault $cmd
done
# File
for cmd in "
awk cat diff diff3
emacs ex file ftp g++ gcc gvim head hg java
javac ld less more mvim patch perl python ruby
sed sftp sort source tail tee uniq vi view vim wc"; do
complete -F _fzf_file_completion -o default -o bashdefault $cmd
done
# Anything
for cmd in "
basename bunzip2 bzip2 chmod chown curl cp dirname du
find git grep gunzip gzip hg jar
ln ls mv open rm rsync scp
svn tar unzip zip"; do
complete -F _fzf_all_completion -o default -o bashdefault $cmd
done
# Kill completion
complete -F _fzf_kill_completion -o nospace -o default -o bashdefault kill
# Host completion
complete -F _fzf_ssh_completion -o default -o bashdefault ssh
complete -F _fzf_telnet_completion -o default -o bashdefault telnet

9
fzf-completion.zsh Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/zsh
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/-completion.zsh
#
# TODO

View File

@@ -1,7 +1,7 @@
# coding: utf-8
Gem::Specification.new do |spec|
spec.name = 'fzf'
spec.version = '0.4.0'
spec.version = '0.8.3'
spec.authors = ['Junegunn Choi']
spec.email = ['junegunn.c@gmail.com']
spec.description = %q{Fuzzy finder for your shell}
@@ -12,4 +12,6 @@ Gem::Specification.new do |spec|
spec.bindir = '.'
spec.files = %w[fzf.gemspec]
spec.executables = 'fzf'
spec.extensions += ['ext/mkrf_conf.rb']
end

259
install
View File

@@ -1,7 +1,260 @@
#!/bin/bash
cd `dirname $BASH_SOURCE`
mkdir -p ~/bin
ln -sf `pwd`/fzf ~/bin/fzf
chmod +x ~/bin/fzf
fzf_base=`pwd`
# ruby executable
echo -n "Checking Ruby executable ... "
ruby=`which ruby`
if [ $? -ne 0 ]; then
echo "ruby executable not found!"
exit 1
fi
# System ruby is preferred
curses_check="begin; require 'curses'; rescue Exception; exit 1; end"
system_ruby=/usr/bin/ruby
if [ -x $system_ruby -a $system_ruby != "$ruby" ]; then
$system_ruby --disable-gems -e "$curses_check" 2> /dev/null
[ $? -eq 0 ] && ruby=$system_ruby
fi
echo "OK ($ruby)"
# Curses-support
echo -n "Checking Curses support ... "
"$ruby" -e "$curses_check"
if [ $? -eq 0 ]; then
echo "OK"
else
echo "Not found"
echo "Installing 'curses' gem ... "
/usr/bin/env gem install curses -v 1.0.0 --user-install
if [ $? -ne 0 ]; then
echo
echo "Failed to install 'curses' gem."
if [[ $(uname -r) =~ 'ARCH' ]]; then
echo "Make sure that base-devel package group is installed."
fi
exit 1
fi
fi
# Ruby version
echo -n "Checking Ruby version ... "
"$ruby" -e 'exit RUBY_VERSION >= "1.9"'
if [ $? -eq 0 ]; then
echo ">= 1.9"
"$ruby" --disable-gems -e "$curses_check"
if [ $? -eq 0 ]; then
fzf_cmd="$ruby --disable-gems $fzf_base/fzf"
else
fzf_cmd="$ruby $fzf_base/fzf"
fi
else
echo "< 1.9"
fzf_cmd="$ruby $fzf_base/fzf"
fi
# Auto-completion
read -p "Do you want to add auto-completion support? ([y]/n) " -n 1 -r
echo
[[ ! $REPLY =~ ^[Nn]$ ]]
auto_completion=$?
# Key-bindings
read -p "Do you want to add key bindings? ([y]/n) " -n 1 -r
echo
[[ ! $REPLY =~ ^[Nn]$ ]]
key_bindings=$?
echo
for shell in bash zsh; do
echo -n "Generate ~/.fzf.$shell ... "
src=~/.fzf.${shell}
fzf_completion="source $fzf_base/fzf-completion.${shell}"
if [ $auto_completion -ne 0 ]; then
fzf_completion="# $fzf_completion"
fi
cat > $src << EOF
# Setup fzf function
# ------------------
unalias fzf 2> /dev/null
fzf() {
$fzf_cmd "\$@"
}
export -f fzf > /dev/null
# Auto-completion
# ---------------
[[ \$- =~ i ]] && $fzf_completion
EOF
if [ $key_bindings -eq 0 ]; then
if [ $shell = bash ]; then
cat >> $src << "EOFZF"
# Key bindings
# ------------
__fsel() {
find * -path '*/\.*' -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | fzf -m | while read item; do
printf '%q ' "$item"
done
echo
}
if [[ $- =~ i ]]; then
__fsel_tmux() {
local height
height=${FZF_TMUX_HEIGHT:-40%}
if [[ $height =~ %$ ]]; then
height="-p ${height%\%}"
else
height="-l $height"
fi
tmux split-window $height "bash -c 'source ~/.fzf.bash; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
}
__fcd() {
local dir
dir=$(find ${1:-*} -path '*/\.*' -prune -o -type d -print 2> /dev/null | fzf +m) && printf 'cd %q' "$dir"
}
__use_tmux=0
[ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ] && __use_tmux=1
if [ -z "$(set -o | grep '^vi.*on')" ]; then
# Required to refresh the prompt after fzf
bind '"\er": redraw-current-line'
# CTRL-T - Paste the selected file path into the command line
if [ $__use_tmux -eq 1 ]; then
bind '"\C-t": " \C-u \C-a\C-k$(__fsel_tmux)\e\C-e\C-y\C-a\C-d\C-y\ey\C-h"'
else
bind '"\C-t": " \C-u \C-a\C-k$(__fsel)\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er"'
fi
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
# ALT-C - cd into the selected directory
bind '"\ec": " \C-e\C-u$(__fcd)\e\C-e\er\C-m"'
else
bind '"\C-x\C-e": shell-expand-line'
bind '"\C-x\C-r": redraw-current-line'
# CTRL-T - Paste the selected file path into the command line
# - FIXME: Selected items are attached to the end regardless of cursor position
if [ $__use_tmux -eq 1 ]; then
bind '"\C-t": "\e$a \eddi$(__fsel_tmux)\C-x\C-e\e0P$xa"'
else
bind '"\C-t": "\e$a \eddi$(__fsel)\C-x\C-e\e0Px$a \C-x\C-r"'
fi
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": "\eddi$(HISTTIMEFORMAT= history | fzf +s | sed \"s/ *[0-9]* *//\")\C-x\C-e\e$a\C-x\C-r"'
# ALT-C - cd into the selected directory
bind '"\ec": "\eddi$(__fcd)\C-x\C-e\C-x\C-r\C-m"'
fi
unset __use_tmux
fi
EOFZF
else
cat >> $src << "EOFZF"
# Key bindings
# ------------
# CTRL-T - Paste the selected file path(s) into the command line
__fsel() {
set -o nonomatch
find * -path '*/\.*' -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | fzf -m | while read item; do
printf '%q ' "$item"
done
echo
}
if [[ $- =~ i ]]; then
if [ -n "$TMUX_PANE" -a ${FZF_TMUX:-1} -ne 0 -a ${LINES:-40} -gt 15 ]; then
fzf-file-widget() {
local height
height=${FZF_TMUX_HEIGHT:-40%}
if [[ $height =~ %$ ]]; then
height="-p ${height%\%}"
else
height="-l $height"
fi
tmux split-window $height "zsh -c 'source ~/.fzf.zsh; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
}
else
fzf-file-widget() {
LBUFFER="${LBUFFER%% #}$(__fsel)"
zle redisplay
}
fi
zle -N fzf-file-widget
bindkey '^T' fzf-file-widget
# ALT-C - cd into the selected directory
fzf-cd-widget() {
cd "${$(set -o nonomatch; find * -path '*/\.*' -prune \
-o -type d -print 2> /dev/null | fzf):-.}"
zle reset-prompt
}
zle -N fzf-cd-widget
bindkey '\ec' fzf-cd-widget
# CTRL-R - Paste the selected command from history into the command line
fzf-history-widget() {
LBUFFER=$(fc -l 1 | fzf +s | sed "s/ *[0-9]* *//")
zle redisplay
}
zle -N fzf-history-widget
bindkey '^R' fzf-history-widget
fi
EOFZF
fi
fi
echo "OK"
done
echo
for shell in bash zsh; do
rc=~/.${shell}rc
src="source ~/.fzf.${shell}"
echo "Update $rc:"
echo " - $src"
line=$(grep -nF "$src" $rc | sed 's/:.*//')
if [ -n "$line" ]; then
echo " - Already exists (line #$line)"
else
echo $src >> $rc
echo " - Added"
fi
echo
done
cat << EOF
Finished. Reload your .bashrc or .zshrc.
source ~/.bashrc # bash
source ~/.zshrc # zsh
To uninstall fzf, simply remove the added line.
For more information, see: https://github.com/junegunn/fzf
EOF

View File

@@ -1,4 +1,4 @@
" Copyright (c) 2013 Junegunn Choi
" Copyright (c) 2014 Junegunn Choi
"
" MIT License
"
@@ -21,25 +21,187 @@
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
let s:exec = expand('<sfile>:h:h').'/fzf'
let s:min_tmux_height = 3
let s:default_tmux_height = '40%'
function! s:fzf(args)
try
let tf = tempname()
let prefix = exists('g:fzf_command') ? g:fzf_command.'|' : ''
let fzf = executable(s:exec) ? s:exec : 'fzf'
execute "silent !".prefix.fzf." ".a:args." > ".tf
if !v:shell_error
let file = join(readfile(tf), '')
if !empty(file)
execute 'silent e '.file
endif
endif
finally
silent! call delete(tf)
redraw!
endtry
let s:cpo_save = &cpo
set cpo&vim
call system('type fzf')
if v:shell_error
let s:fzf_rb = expand('<sfile>:h:h').'/fzf'
if executable(s:fzf_rb)
let s:exec = s:fzf_rb
else
echoerr 'fzf executable not found'
finish
endif
else
let s:exec = 'fzf'
endif
function! s:tmux_enabled()
if exists('s:tmux')
return s:tmux
endif
let s:tmux = 0
if exists('$TMUX')
let output = system('tmux -V')
let s:tmux = !v:shell_error && output >= 'tmux 1.7'
endif
return s:tmux
endfunction
command! -nargs=* FZF call s:fzf(<q-args>)
function! s:shellesc(arg)
return '"'.substitute(a:arg, '"', '\\"', 'g').'"'
endfunction
function! s:escape(path)
return substitute(a:path, ' ', '\\ ', 'g')
endfunction
function! fzf#run(...) abort
if has('gui_running')
echohl Error
echo 'GVim is not supported'
return []
endif
let dict = exists('a:1') ? a:1 : {}
let temps = { 'result': tempname() }
let optstr = get(dict, 'options', '')
if has_key(dict, 'source')
let source = dict.source
let type = type(source)
if type == 1
let prefix = source.'|'
elseif type == 3
let temps.input = tempname()
call writefile(source, temps.input)
let prefix = 'cat '.s:shellesc(temps.input).'|'
else
throw 'Invalid source type'
endif
else
let prefix = ''
endif
let command = prefix.s:exec.' '.optstr.' > '.temps.result
if s:tmux_enabled() && has_key(dict, 'tmux') &&
\ dict.tmux > 0 && winheight(0) >= s:min_tmux_height
return s:execute_tmux(dict, command, temps)
else
return s:execute(dict, command, temps)
endif
endfunction
function! s:pushd(dict)
if has_key(a:dict, 'dir')
let a:dict.prev_dir = getcwd()
execute 'chdir '.s:escape(a:dict.dir)
endif
endfunction
function! s:popd(dict)
if has_key(a:dict, 'prev_dir')
execute 'chdir '.s:escape(remove(a:dict, 'prev_dir'))
endif
endfunction
function! s:execute(dict, command, temps)
call s:pushd(a:dict)
silent !clear
execute 'silent !'.a:command
redraw!
if v:shell_error
return []
else
return s:callback(a:dict, a:temps, 0)
endif
endfunction
function! s:execute_tmux(dict, command, temps)
if has_key(a:dict, 'dir')
let command = 'cd '.s:escape(a:dict.dir).' && '.a:command
else
let command = a:command
endif
if type(a:dict.tmux) == 1 && a:dict.tmux =~ '%$'
let height = '-p '.a:dict.tmux[0:-2]
else
let height = '-l '.a:dict.tmux
endif
let s:pane = substitute(
\ system(
\ printf(
\ 'tmux split-window %s -P -F "#{pane_id}" %s',
\ height, s:shellesc(command))), '\n', '', 'g')
let s:dict = a:dict
let s:temps = a:temps
augroup fzf_tmux
autocmd!
autocmd VimResized * nested call s:tmux_check()
augroup END
endfunction
function! s:tmux_check()
let panes = split(system('tmux list-panes -a -F "#{pane_id}"'), '\n')
if index(panes, s:pane) < 0
augroup fzf_tmux
autocmd!
augroup END
call s:callback(s:dict, s:temps, 1)
redraw
endif
endfunction
function! s:callback(dict, temps, cd)
if !filereadable(a:temps.result)
let lines = []
else
if a:cd | call s:pushd(a:dict) | endif
let lines = readfile(a:temps.result)
if has_key(a:dict, 'sink')
for line in lines
if type(a:dict.sink) == 2
call a:dict.sink(line)
else
execute a:dict.sink.' '.s:escape(line)
endif
endfor
endif
endif
for tf in values(a:temps)
silent! call delete(tf)
endfor
call s:popd(a:dict)
return lines
endfunction
function! s:cmd(bang, ...) abort
let args = copy(a:000)
let opts = {}
if len(args) > 0 && isdirectory(expand(args[-1]))
let opts.dir = remove(args, -1)
endif
if !a:bang
let opts.tmux = get(g:, 'fzf_tmux_height', s:default_tmux_height)
endif
call fzf#run(extend({ 'sink': 'e', 'options': join(args) }, opts))
endfunction
command! -nargs=* -complete=dir -bang FZF call s:cmd('<bang>' == '!', <f-args>)
let &cpo = s:cpo_save
unlet s:cpo_save

37
test/fzf.vader Normal file
View File

@@ -0,0 +1,37 @@
Execute (Setup):
let g:dir = fnamemodify(g:vader_file, ':p:h')
Log 'Test directory: ' . g:dir
Execute (fzf#run with dir option):
let result = fzf#run({ 'options': '--filter=vdr', 'dir': g:dir })
AssertEqual ['fzf.vader'], result
let result = sort(fzf#run({ 'options': '--filter e', 'dir': g:dir }))
AssertEqual ['fzf.vader', 'test_fzf.rb'], result
Execute (fzf#run with Funcref command):
let g:ret = []
function! g:proc(e)
call add(g:ret, a:e)
endfunction
let result = sort(fzf#run({ 'sink': function('g:proc'), 'options': '--filter e', 'dir': g:dir }))
AssertEqual ['fzf.vader', 'test_fzf.rb'], result
AssertEqual ['fzf.vader', 'test_fzf.rb'], sort(g:ret)
Execute (fzf#run with string source):
let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
AssertEqual ['hi'], result
Execute (fzf#run with list source):
let result = sort(fzf#run({ 'source': ['hello', 'world'], 'options': '-f e' }))
AssertEqual ['hello'], result
let result = sort(fzf#run({ 'source': ['hello', 'world'], 'options': '-f o' }))
AssertEqual ['hello', 'world'], result
Execute (fzf#run with string source):
let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
AssertEqual ['hi'], result
Execute (Cleanup):
unlet g:dir
Restore

View File

@@ -1,50 +1,151 @@
#!/usr/bin/env ruby
# encoding: utf-8
require 'curses'
require 'timeout'
require 'stringio'
require 'minitest/autorun'
$LOAD_PATH.unshift File.expand_path('../..', __FILE__)
ENV['FZF_EXECUTABLE'] = '0'
load 'fzf'
class TestFZF < MiniTest::Unit::TestCase
def setup
ENV.delete 'FZF_DEFAULT_SORT'
ENV.delete 'FZF_DEFAULT_OPTS'
ENV.delete 'FZF_DEFAULT_COMMAND'
end
def test_default_options
fzf = FZF.new []
assert_equal 1000, fzf.sort
assert_equal 1000, fzf.sort
assert_equal false, fzf.multi
assert_equal true, fzf.color
assert_equal Regexp::IGNORECASE, fzf.rxflag
assert_equal true, fzf.color
assert_equal nil, fzf.rxflag
assert_equal true, fzf.mouse
assert_equal nil, fzf.nth
assert_equal true, fzf.color
assert_equal false, fzf.black
assert_equal true, fzf.ansi256
assert_equal '', fzf.query.get
assert_equal false, fzf.select1
assert_equal false, fzf.exit0
assert_equal nil, fzf.filter
assert_equal nil, fzf.extended
end
begin
ENV['FZF_DEFAULT_SORT'] = '1500'
fzf = FZF.new []
assert_equal 1500, fzf.sort
ensure
ENV.delete 'FZF_DEFAULT_SORT'
end
def test_environment_variables
# Deprecated
ENV['FZF_DEFAULT_SORT'] = '20000'
fzf = FZF.new []
assert_equal 20000, fzf.sort
assert_equal nil, fzf.nth
ENV['FZF_DEFAULT_OPTS'] =
'-x -m -s 10000 -q " hello world " +c +2 --select-1 -0 ' +
'--no-mouse -f "goodbye world" --black --nth=3,-1,2'
fzf = FZF.new []
assert_equal 10000, fzf.sort
assert_equal ' hello world ',
fzf.query.get
assert_equal 'goodbye world',
fzf.filter
assert_equal :fuzzy, fzf.extended
assert_equal true, fzf.multi
assert_equal false, fzf.color
assert_equal false, fzf.ansi256
assert_equal true, fzf.black
assert_equal false, fzf.mouse
assert_equal true, fzf.select1
assert_equal true, fzf.exit0
assert_equal [3, -1, 2], fzf.nth
end
def test_option_parser
# Long opts
fzf = FZF.new %w[--sort=2000 --no-color --multi +i]
assert_equal 2000, fzf.sort
assert_equal true, fzf.multi
assert_equal false, fzf.color
assert_equal 0, fzf.rxflag
fzf = FZF.new %w[--sort=2000 --no-color --multi +i --query hello --select-1
--exit-0 --filter=howdy --extended-exact
--no-mouse --no-256 --nth=1]
assert_equal 2000, fzf.sort
assert_equal true, fzf.multi
assert_equal false, fzf.color
assert_equal false, fzf.ansi256
assert_equal false, fzf.black
assert_equal false, fzf.mouse
assert_equal 0, fzf.rxflag
assert_equal 'hello', fzf.query.get
assert_equal true, fzf.select1
assert_equal true, fzf.exit0
assert_equal 'howdy', fzf.filter
assert_equal :exact, fzf.extended
assert_equal [1], fzf.nth
# Long opts (left-to-right)
fzf = FZF.new %w[--sort=2000 --no-color --multi +i --query=hello
--filter a --filter b --no-256 --black --nth -1 --nth -2
--select-1 --exit-0 --no-select-1 --no-exit-0
--no-sort -i --color --no-multi --256]
assert_equal nil, fzf.sort
assert_equal false, fzf.multi
assert_equal true, fzf.color
assert_equal true, fzf.ansi256
assert_equal true, fzf.black
assert_equal true, fzf.mouse
assert_equal 1, fzf.rxflag
assert_equal 'b', fzf.filter
assert_equal 'hello', fzf.query.get
assert_equal false, fzf.select1
assert_equal false, fzf.exit0
assert_equal nil, fzf.extended
assert_equal [-2], fzf.nth
# Short opts
fzf = FZF.new %w[-s 2000 +c -m +i]
assert_equal 2000, fzf.sort
assert_equal true, fzf.multi
assert_equal false, fzf.color
assert_equal 0, fzf.rxflag
fzf = FZF.new %w[-s2000 +c -m +i -qhello -x -fhowdy +2 -n3 -1 -0]
assert_equal 2000, fzf.sort
assert_equal true, fzf.multi
assert_equal false, fzf.color
assert_equal false, fzf.ansi256
assert_equal 0, fzf.rxflag
assert_equal 'hello', fzf.query.get
assert_equal 'howdy', fzf.filter
assert_equal :fuzzy, fzf.extended
assert_equal [3], fzf.nth
assert_equal true, fzf.select1
assert_equal true, fzf.exit0
# Left-to-right
fzf = FZF.new %w[-s 2000 +c -m +i -qhello -x -fgoodbye +2 -n3 -n4,5
-s 3000 -c +m -i -q world +x -fworld -2 --black --no-black
-1 -0 +1 +0
]
assert_equal 3000, fzf.sort
assert_equal false, fzf.multi
assert_equal true, fzf.color
assert_equal true, fzf.ansi256
assert_equal false, fzf.black
assert_equal 1, fzf.rxflag
assert_equal 'world', fzf.query.get
assert_equal false, fzf.select1
assert_equal false, fzf.exit0
assert_equal 'world', fzf.filter
assert_equal nil, fzf.extended
assert_equal [4, 5], fzf.nth
rescue SystemExit => e
assert false, "Exited"
end
def test_invalid_option
[%w[-s 2000 +s], %w[yo dawg]].each do |argv|
[%w[--unknown], %w[yo dawg]].each do |argv|
assert_raises(SystemExit) do
fzf = FZF.new argv
end
end
assert_raises(SystemExit) do
fzf = FZF.new %w[--nth=0]
end
assert_raises(SystemExit) do
fzf = FZF.new %w[-n 0]
end
end
# FIXME Only on 1.9 or above
@@ -139,15 +240,59 @@ class TestFZF < MiniTest::Unit::TestCase
# TODO : partial_cache
end
def test_fuzzy_matcher_rxflag
assert_equal nil, FZF::FuzzyMatcher.new(nil).rxflag
assert_equal 0, FZF::FuzzyMatcher.new(0).rxflag
assert_equal 1, FZF::FuzzyMatcher.new(1).rxflag
assert_equal 1, FZF::FuzzyMatcher.new(nil).rxflag_for('abc')
assert_equal 0, FZF::FuzzyMatcher.new(nil).rxflag_for('Abc')
assert_equal 0, FZF::FuzzyMatcher.new(0).rxflag_for('abc')
assert_equal 0, FZF::FuzzyMatcher.new(0).rxflag_for('Abc')
assert_equal 1, FZF::FuzzyMatcher.new(1).rxflag_for('abc')
assert_equal 1, FZF::FuzzyMatcher.new(1).rxflag_for('Abc')
end
def test_fuzzy_matcher_case_sensitive
# Smart-case match (Uppercase found)
assert_equal [['Fruit', [[0, 5]]]],
FZF::FuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], 'Fruit', '', '').sort
# Smart-case match (Uppercase not-found)
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
FZF::FuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], 'fruit', '', '').sort
# Case-sensitive match (-i)
assert_equal [['Fruit', [[0, 5]]]],
FZF::FuzzyMatcher.new(0).match(%w[Fruit Grapefruit], 'Fruit', '', '').sort
# Case-insensitive match (+i)
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
FZF::FuzzyMatcher.new(Regexp::IGNORECASE).
match(%w[Fruit Grapefruit], 'Fruit', '', '').sort
end
def test_extended_fuzzy_matcher_case_sensitive
%w['Fruit Fruit$].each do |q|
# Smart-case match (Uppercase found)
assert_equal [['Fruit', [[0, 5]]]],
FZF::ExtendedFuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], q, '', '').sort
# Smart-case match (Uppercase not-found)
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
FZF::ExtendedFuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], q.downcase, '', '').sort
# Case-sensitive match (-i)
assert_equal [['Fruit', [[0, 5]]]],
FZF::ExtendedFuzzyMatcher.new(0).match(%w[Fruit Grapefruit], q, '', '').sort
# Case-insensitive match (+i)
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
FZF::ExtendedFuzzyMatcher.new(Regexp::IGNORECASE).
match(%w[Fruit Grapefruit], q, '', '').sort
end
end
def test_extended_fuzzy_matcher
matcher = FZF::ExtendedFuzzyMatcher.new Regexp::IGNORECASE
list = %w[
@@ -184,6 +329,11 @@ class TestFZF < MiniTest::Unit::TestCase
assert_equal list.length, match.call('j', '').length
assert_equal list.length - 1, match.call('^j', '').length
# ^ + $
assert_equal 0, match.call('^juici$', '').length
assert_equal 1, match.call('^juice$', '').length
assert_equal 0, match.call('^.*$', '').length
# !
assert_equal 0, match.call('!j', '').length
@@ -250,7 +400,7 @@ class TestFZF < MiniTest::Unit::TestCase
["0____1", [[0, 6]]],
["0_____1", [[0, 7]]],
["0______1", [[0, 8]]]],
FZF.new([]).sort_by_rank(matcher.match(list, '01', '', '')))
FZF.sort(matcher.match(list, '01', '', '')))
assert_equal(
[["01", [[0, 1], [1, 2]]],
@@ -261,16 +411,38 @@ class TestFZF < MiniTest::Unit::TestCase
["____0_1", [[4, 5], [6, 7]]],
["0______1", [[0, 1], [7, 8]]],
["___01___", [[3, 4], [4, 5]]]],
FZF.new([]).sort_by_rank(xmatcher.match(list, '0 1', '', '')))
FZF.sort(xmatcher.match(list, '0 1', '', '')))
assert_equal(
[["_01_", [[1, 3], [0, 4]]],
["0____1", [[0, 6], [1, 3]]],
["0_____1", [[0, 7], [1, 3]]],
["0______1", [[0, 8], [1, 3]]],
["___01___", [[3, 5], [0, 2]]],
["____0_1", [[4, 7], [0, 2]]]],
FZF.new([]).sort_by_rank(xmatcher.match(list, '01 __', '', '')))
[["_01_", [[1, 3], [0, 4]], [4, 4, "_01_"]],
["___01___", [[3, 5], [0, 2]], [4, 8, "___01___"]],
["____0_1", [[4, 7], [0, 2]], [5, 7, "____0_1"]],
["0____1", [[0, 6], [1, 3]], [6, 6, "0____1"]],
["0_____1", [[0, 7], [1, 3]], [7, 7, "0_____1"]],
["0______1", [[0, 8], [1, 3]], [8, 8, "0______1"]]],
FZF.sort(xmatcher.match(list, '01 __', '', '')).map { |tuple|
tuple << FZF.rank(tuple)
}
)
end
def test_extended_exact_mode
exact = FZF::ExtendedFuzzyMatcher.new Regexp::IGNORECASE, :exact
fuzzy = FZF::ExtendedFuzzyMatcher.new Regexp::IGNORECASE, :fuzzy
list = %w[
extended-exact-mode-not-fuzzy
extended'-fuzzy-mode
]
assert_equal 2, fuzzy.match(list, 'extended', '', '').length
assert_equal 2, fuzzy.match(list, 'mode extended', '', '').length
assert_equal 2, fuzzy.match(list, 'xtndd', '', '').length
assert_equal 2, fuzzy.match(list, "'-fuzzy", '', '').length
assert_equal 2, exact.match(list, 'extended', '', '').length
assert_equal 2, exact.match(list, 'mode extended', '', '').length
assert_equal 0, exact.match(list, 'xtndd', '', '').length
assert_equal 1, exact.match(list, "'-fuzzy", '', '').length
assert_equal 2, exact.match(list, "-fuzzy", '', '').length
end
if RUBY_PLATFORM =~ /darwin/
@@ -293,8 +465,25 @@ class TestFZF < MiniTest::Unit::TestCase
def test_nfd
nfc = '한글'
nfd = FZF::UConv.nfd(nfc)
assert_equal 6, nfd.length
assert_equal NFD, nfd
assert_equal 2, nfd.length
assert_equal 6, nfd.join.length
assert_equal NFD, nfd.join
end
def test_nfd_fuzzy_matcher
matcher = FZF::FuzzyMatcher.new 0
assert_equal [], matcher.match([NFD + NFD], '할', '', '')
match = matcher.match([NFD + NFD], '글글', '', '')
assert_equal [[NFD + NFD, [[3, 12]]]], match
assert_equal ['한글한글', [[1, 4]]], FZF::UConv.nfc(*match.first)
end
def test_nfd_extended_fuzzy_matcher
matcher = FZF::ExtendedFuzzyMatcher.new 0
assert_equal [], matcher.match([NFD], "'글글", '', '')
match = matcher.match([NFD], "'한글", '', '')
assert_equal [[NFD, [[0, 6]]]], match
assert_equal ['한글', [[0, 2]]], FZF::UConv.nfc(*match.first)
end
end
@@ -302,5 +491,175 @@ class TestFZF < MiniTest::Unit::TestCase
assert_equal ["a", "b", "c", "\xFF", "d", "e", "f"],
FZF::UConv.split("abc\xFFdef")
end
# ^$ -> matches empty item
def test_format_empty_item
fzf = FZF.new []
item = ['', [[0, 0]]]
line, offsets = fzf.convert_item item
tokens = fzf.format line, 80, offsets
assert_equal [], tokens
end
def test_mouse_event
interval = FZF::MouseEvent::DOUBLE_CLICK_INTERVAL
me = FZF::MouseEvent.new nil
me.v = 10
assert_equal false, me.double?(10)
assert_equal false, me.double?(20)
me.v = 20
assert_equal false, me.double?(10)
assert_equal false, me.double?(20)
me.v = 20
assert_equal false, me.double?(10)
assert_equal true, me.double?(20)
sleep interval
assert_equal false, me.double?(20)
end
def test_nth_match
list = [
' first second third',
'fourth fifth sixth',
]
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE
assert_equal list, matcher.match(list, 'f', '', '').map(&:first)
assert_equal [
[list[0], [[2, 5]]],
[list[1], [[9, 17]]]], matcher.match(list, 'is', '', '')
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2]
assert_equal [[list[1], [[8, 9]]]], matcher.match(list, 'f', '', '')
assert_equal [[list[0], [[8, 9]]]], matcher.match(list, 's', '', '')
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3]
assert_equal [[list[0], [[19, 20]]]], matcher.match(list, 'r', '', '')
# Comma-separated
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3, 1]
assert_equal [[list[0], [[19, 20]]], [list[1], [[3, 4]]]], matcher.match(list, 'r', '', '')
# Ordered
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1, 3]
assert_equal [[list[0], [[3, 4]]], [list[1], [[3, 4]]]], matcher.match(list, 'r', '', '')
regex = FZF.build_delim_regex "\t"
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1], regex
assert_equal [[list[0], [[3, 10]]]], matcher.match(list, 're', '', '')
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex
assert_equal [], matcher.match(list, 'r', '', '')
assert_equal [[list[1], [[9, 17]]]], matcher.match(list, 'is', '', '')
# Negative indexing
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [-1], regex
assert_equal [[list[0], [[3, 6]]]], matcher.match(list, 'rt', '', '')
assert_equal [[list[0], [[2, 5]]], [list[1], [[9, 17]]]], matcher.match(list, 'is', '', '')
# Regex delimiter
regex = FZF.build_delim_regex "[ \t]+"
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1], regex
assert_equal [list[1]], matcher.match(list, 'f', '', '').map(&:first)
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex
assert_equal [[list[0], [[1, 2]]], [list[1], [[8, 9]]]], matcher.match(list, 'f', '', '')
end
def stream_for str
StringIO.new(str).tap do |sio|
sio.instance_eval do
alias org_gets gets
def gets
org_gets.tap { |e| sleep 0.5 unless e.nil? }
end
end
end
end
def test_select_1
stream = stream_for "Hello\nWorld"
output = StringIO.new
begin
$stdout = output
FZF.new(%w[--query=ol --select-1], stream).start
rescue SystemExit => e
assert_equal 0, e.status
assert_equal 'World', output.string.chomp
ensure
$stdout = STDOUT
end
end
def test_select_1_without_query
stream = stream_for "Hello World"
output = StringIO.new
begin
$stdout = output
FZF.new(%w[--select-1], stream).start
rescue SystemExit => e
assert_equal 0, e.status
assert_equal 'Hello World', output.string.chomp
ensure
$stdout = STDOUT
end
end
def test_select_1_ambiguity
stream = stream_for "Hello\nWorld"
begin
Timeout::timeout(3) do
FZF.new(%w[--query=o --select-1], stream).start
end
flunk 'Should not reach here'
rescue Exception => e
Curses.close_screen
assert_instance_of Timeout::Error, e
end
end
def test_exit_0
stream = stream_for "Hello\nWorld"
output = StringIO.new
begin
$stdout = output
FZF.new(%w[--query=zz --exit-0], stream).start
rescue SystemExit => e
assert_equal 0, e.status
assert_equal '', output.string
ensure
$stdout = STDOUT
end
end
def test_exit_0_without_query
stream = stream_for ""
output = StringIO.new
begin
$stdout = output
FZF.new(%w[--exit-0], stream).start
rescue SystemExit => e
assert_equal 0, e.status
assert_equal '', output.string
ensure
$stdout = STDOUT
end
end
def test_ranking_overlap_match_regions
list = [
'1 3 4 2',
'1 2 3 4'
]
assert_equal [
['1 2 3 4', [[0, 13], [16, 22]]],
['1 3 4 2', [[0, 24], [12, 17]]],
], FZF.sort(FZF::ExtendedFuzzyMatcher.new(nil).match(list, '12 34', '', ''))
end
end