m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-16 23:33:39 -05:00

Compare commits

..

20 Commits

Author SHA1 Message Date
Junegunn Choi
305ec3b3ce [fish] Remove buffering delay by not using subroutines
Close #169
2015-04-22 14:33:03 +09:00
Junegunn Choi
f4fe93338b Update README 2015-04-22 02:09:16 +09:00
Junegunn Choi
3b84c80d56 Update README 2015-04-22 02:07:27 +09:00
Junegunn Choi
5e120e7ab5 Update man page 2015-04-22 01:44:56 +09:00
Junegunn Choi
a4cf5510e3 0.9.11 2015-04-22 01:42:38 +09:00
Junegunn Choi
edb5ab5622 Update test cases for #203 2015-04-22 00:57:25 +09:00
Junegunn Choi
06b4f75680 Fix broken FZF_TMUX switch and update test cases (#203) 2015-04-22 00:55:39 +09:00
Junegunn Choi
318edc8c35 Apply fzf-tmux to key bindings (#203)
Note that CTRL-T on bash is still using the old trick of send-keys.
2015-04-22 00:32:18 +09:00
Junegunn Choi
651a8f8cc2 Add --inline-info option
Close #202
2015-04-21 23:50:53 +09:00
Junegunn Choi
9f64a00549 Fix double-click result when scroll offset is positive 2015-04-21 23:23:39 +09:00
Junegunn Choi
a88bf87e2a Update test case 2015-04-21 22:36:40 +09:00
Junegunn Choi
e82eb27787 Smart-case for each term in extended-search mode
Close #208
2015-04-21 22:18:05 +09:00
Junegunn Choi
3f0e6a5806 Fix #209 - Invalid mutation of input on case conversion 2015-04-21 22:10:14 +09:00
Junegunn Choi
917b1759b0 [fzf-tmux/vim] Fixes for fish (#204) 2015-04-20 22:42:12 +09:00
Junegunn Choi
16ca9c688b Revert "[fzf-tmux] Fix #204 - Escape command substitution"
This reverts commit 7b6a27cb5e.
2015-04-20 16:23:15 +09:00
Junegunn Choi
7b6a27cb5e [fzf-tmux] Fix #204 - Escape command substitution 2015-04-20 15:22:59 +09:00
Junegunn Choi
869a234938 [fzf-tmux] Use bash instead of sh (#204)
The default shell can be a non-standard shell (e.g. fish)
2015-04-20 14:58:27 +09:00
Junegunn Choi
537d07c1e5 [vim] Use "system" fzf when available
1. Go binary: ../bin/fzf
2. System fzf: $(which fzf)
3. Download fzf from GitHub or create wrapper script to Ruby version (../fzf)
   when the binary for the platform is not available
4. If install script is not found or for some reason failed, try to use Ruby
   version in its expected location (../fzf)
5. If fzf is found to be a shell function, use it (type fzf)
2015-04-19 17:13:07 +09:00
Junegunn Choi
d091a2c4bb [fzf-tmux] Minor adjustment 2015-04-18 16:27:40 +09:00
Junegunn Choi
d2f95d69fb [fzf-tmux] Fix #200 - Double-quote handling
Related #199
2015-04-18 16:24:57 +09:00
17 changed files with 224 additions and 226 deletions

View File

@@ -1,6 +1,21 @@
CHANGELOG
=========
0.9.11
------
### New features
- Added `--inline-info` option for saving screen estate (#202)
- Useful inside Neovim
- e.g. `let $FZF_DEFAULT_OPTS = $FZF_DEFAULT_OPTS.' --inline-info'`
### Bug fixes
- Invalid mutation of input on case conversion (#209)
- Smart-case for each term in extended-search mode (#208)
- Fixed double-click result when scroll offset is positive
0.9.10
------

View File

@@ -45,17 +45,6 @@ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
```
#### Using curl
In case you don't have git installed:
```sh
mkdir -p ~/.fzf
curl -L https://github.com/junegunn/fzf/archive/master.tar.gz |
tar xz --strip-components 1 -C ~/.fzf
~/.fzf/install
```
#### Using Homebrew
On OS X, you can use [Homebrew](http://brew.sh/) to install fzf.
@@ -157,18 +146,14 @@ fish.
- Press `CTRL-R` again to toggle sort
- `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%`).
If you're on a tmux session, fzf will start in a split pane. You may disable
this tmux integration by setting `FZF_TMUX` to 0, or change the height of the
pane with `FZF_TMUX_HEIGHT` (e.g. `20`, `50%`).
If you use vi mode on bash, you need to add `set -o vi` *before* `source
~/.fzf.bash` in your .bashrc, so that it correctly sets up key bindings for vi
mode.
If you want to customize the key bindings, consider editing the
installer-generated source code: `~/.fzf.bash`, `~/.fzf.zsh`, and
`~/.config/fish/functions/fzf_key_bindings.fish`.
`fzf-tmux` script
-----------------
@@ -286,10 +271,10 @@ Similarly to [ctrlp.vim](https://github.com/kien/ctrlp.vim), use enter key,
in new tabs, in horizontal splits, or in vertical splits respectively.
Note that the environment variables `FZF_DEFAULT_COMMAND` and
`FZF_DEFAULT_OPTS` also apply here. Refer to [the wiki page][vim-examples] for
`FZF_DEFAULT_OPTS` also apply here. Refer to [the wiki page][fzf-config] for
customization.
[vim-examples]: https://github.com/junegunn/fzf/wiki/Examples-(vim)
[fzf-config]: https://github.com/junegunn/fzf/wiki/Configuring-FZF-command-(vim)
#### `fzf#run([options])`
@@ -372,10 +357,6 @@ nnoremap <silent> <Leader><Enter> :call fzf#run({
More examples can be found on [the wiki
page](https://github.com/junegunn/fzf/wiki/Examples-(vim)).
#### Articles
- [fzf+vim+tmux](http://junegunn.kr/2014/04/fzf+vim+tmux)
Tips
----
@@ -384,13 +365,13 @@ Tips
If you have any rendering issues, check the followings:
1. Make sure `$TERM` is correctly set. fzf will use 256-color only if it
contains `256` (e.g. `xterm-256color`)
contains `256` (e.g. `xterm-256color`)
2. If you're on screen or tmux, `$TERM` should be either `screen` or
`screen-256color`
`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.
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`.
#### Respecting `.gitignore`, `.hgignore`, and `svn:ignore`
@@ -421,41 +402,6 @@ export FZF_DEFAULT_COMMAND='
find * -name ".*" -prune -o -type f -print -o -type l -print) 2> /dev/null'
```
#### Using fzf with tmux panes
The supplied [fzf-tmux](bin/fzf-tmux) script should suffice in most of the
cases, but if you want to be able to update command line like the default
`CTRL-T` key binding, you'll have to use `send-keys` command of tmux. The
following example will show you how it can be done.
```sh
# This is a helper function that splits the current pane to start the given
# command ($1) and sends its output back to the original pane with any number of
# optional keys (shift; $*).
fzf_tmux_helper() {
[ -n "$TMUX_PANE" ] || return
local cmd=$1
shift
tmux split-window -p 40 \
"bash -c \"\$(tmux send-keys -t $TMUX_PANE \"\$(source ~/.fzf.bash; $cmd)\" $*)\""
}
# This is the function we are going to run in the split pane.
# - "find" to list the directories
# - "sed" will escape spaces in the paths.
# - "paste" will join the selected paths into a single line
fzf_tmux_dir() {
fzf_tmux_helper \
'find * -path "*/\.*" -prune -o -type d -print 2> /dev/null |
fzf --multi |
sed "s/ /\\\\ /g" |
paste -sd" " -' Space
}
# Bind CTRL-X-CTRL-D to fzf_tmux_dir
bind '"\C-x\C-d": "$(fzf_tmux_dir)\e\C-e"'
```
#### Fish shell
It's [a known bug of fish](https://github.com/fish-shell/fish-shell/issues/1362)
@@ -464,19 +410,7 @@ simple `vim (fzf)` won't work as expected. The workaround is to store the result
of fzf to a temporary file.
```sh
function vimf
if fzf > $TMPDIR/fzf.result
vim (cat $TMPDIR/fzf.result)
end
end
function fe
set tmp $TMPDIR/fzf.result
fzf --query="$argv[1]" --select-1 --exit-0 > $tmp
if [ (cat $tmp | wc -l) -gt 0 ]
vim (cat $tmp)
end
end
fzf > $TMPDIR/fzf.result; and vim (cat $TMPDIR/fzf.result)
```
#### Handling UTF-8 NFD paths on OSX

View File

@@ -89,16 +89,14 @@ fi
set -e
# Build arguments to fzf
[ ${#args[@]} -gt 0 ] && fzf_args=$(printf '\\"%s\\" ' "${args[@]}"; echo '')
# Clean up named pipes on exit
id=$RANDOM
argsf=/tmp/fzf-args-$id
fifo1=/tmp/fzf-fifo1-$id
fifo2=/tmp/fzf-fifo2-$id
fifo3=/tmp/fzf-fifo3-$id
cleanup() {
rm -f $fifo1 $fifo2 $fifo3
rm -f $argsf $fifo1 $fifo2 $fifo3
}
trap cleanup EXIT SIGINT SIGTERM
@@ -115,13 +113,22 @@ envs=""
mkfifo $fifo2
mkfifo $fifo3
# Build arguments to fzf
opts=""
for arg in "${args[@]}"; do
opts="$opts \"${arg//\"/\\\"}\""
done
if [ -n "$term" -o -t 0 ]; then
cat <<< "$fzf $opts > $fifo2; echo \$? > $fifo3 $close" > $argsf
tmux set-window-option -q synchronize-panes off \;\
split-window $opt "cd $(printf %q "$PWD");$envs"' sh -c "'$fzf' '"$fzf_args"' > '$fifo2'; echo \$? > '$fifo3' '"$close"'"' $swap
split-window $opt "cd $(printf %q "$PWD");$envs bash $argsf" $swap
else
mkfifo $fifo1
cat <<< "$fzf $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" > $argsf
tmux set-window-option -q synchronize-panes off \;\
split-window $opt "$envs"' sh -c "'$fzf' '"$fzf_args"' < '$fifo1' > '$fifo2'; echo \$? > '$fifo3' '"$close"'"' $swap
split-window $opt "$envs bash $argsf" $swap
cat <&0 > $fifo1 &
fi
cat $fifo2

1
fzf
View File

@@ -209,6 +209,7 @@ class FZF
when '--toggle-sort', '--tiebreak', '--color'
argv.shift
when '--tac', '--no-tac', '--sync', '--no-sync', '--hscroll', '--no-hscroll',
'--inline-info', '--no-inline-info',
/^--color=(.*)$/, /^--toggle-sort=(.*)$/, /^--tiebreak=(.*)$/
# XXX
else

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
version=0.9.10
version=0.9.11
cd $(dirname $BASH_SOURCE)
fzf_base=$(pwd)

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH fzf 1 "April 2015" "fzf 0.9.10" "fzf - a command-line fuzzy finder"
.TH fzf 1 "April 2015" "fzf 0.9.11" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@@ -116,6 +116,9 @@ Reverse orientation
.B "--no-hscroll"
Disable horizontal scroll
.TP
.B "--inline-info"
Display finder info inline with the query
.TP
.BI "--prompt=" "STR"
Input prompt (default: '> ')
.SS Scripting

View File

@@ -36,17 +36,17 @@ function! s:fzf_exec()
if !exists('s:exec')
if executable(s:fzf_go)
let s:exec = s:fzf_go
elseif !s:installed && executable(s:install)
echohl WarningMsg
echo 'Downloading fzf binary. Please wait ...'
echohl None
let s:installed = 1
call system(s:install.' --bin')
return s:fzf_exec()
else
let path = split(system('which fzf 2> /dev/null'), '\n')
if !v:shell_error && !empty(path)
let s:exec = path[0]
elseif !s:installed && executable(s:install)
echohl WarningMsg
echo 'Downloading fzf binary. Please wait ...'
echohl None
let s:installed = 1
call system(s:install.' --bin')
return s:fzf_exec()
elseif executable(s:fzf_rb)
let s:exec = s:fzf_rb
else
@@ -105,6 +105,9 @@ function! s:upgrade(dict)
endfunction
function! fzf#run(...) abort
try
let oshell = &shell
set shell=sh
if has('nvim') && bufexists('[FZF]')
echohl WarningMsg
echomsg 'FZF is already running!'
@@ -149,6 +152,9 @@ function! fzf#run(...) abort
finally
call s:popd(dict)
endtry
finally
let &shell = oshell
endtry
endfunction
function! s:present(dict, ...)

View File

@@ -1,6 +1,6 @@
# Key bindings
# ------------
__fsel() {
__fzf_select__() {
command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type f -print \
-o -type d -print \
@@ -12,7 +12,11 @@ __fsel() {
if [[ $- =~ i ]]; then
__fsel_tmux() {
__fzfcmd() {
[ ${FZF_TMUX:-1} -eq 1 ] && echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
}
__fzf_select_tmux__() {
local height
height=${FZF_TMUX_HEIGHT:-40%}
if [[ $height =~ %$ ]]; then
@@ -20,13 +24,17 @@ __fsel_tmux() {
else
height="-l $height"
fi
tmux split-window $height "cd $(printf %q "$PWD");bash -c 'source ~/.fzf.bash; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
tmux split-window $height "cd $(printf %q "$PWD");bash -c 'source ~/.fzf.bash; tmux send-keys -t $TMUX_PANE \"\$(__fzf_select__)\"'"
}
__fcd() {
__fzf_cd__() {
local dir
dir=$(command find -L ${1:-.} \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m) && printf 'cd %q' "$dir"
-o -type d -print 2> /dev/null | sed 1d | cut -b3- | $(__fzfcmd) +m) && printf 'cd %q' "$dir"
}
__fzf_history__() {
HISTTIMEFORMAT= history | $(__fzfcmd) +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r | sed "s/ *[0-9]* *//"
}
__use_tmux=0
@@ -38,16 +46,16 @@ if [ -z "$(set -o | \grep '^vi.*on')" ]; then
# 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"'
bind '"\C-t": " \C-u \C-a\C-k$(__fzf_select_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 \C-h"'
bind '"\C-t": " \C-u \C-a\C-k$(__fzf_select__)\e\C-e\C-y\C-a\C-y\ey\C-h\C-e\er \C-h"'
fi
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": " \C-e\C-u$(HISTTIMEFORMAT= history | fzf +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
bind '"\C-r": " \C-e\C-u$(__fzf_history__)\e\C-e\er"'
# ALT-C - cd into the selected directory
bind '"\ec": " \C-e\C-u$(__fcd)\e\C-e\er\C-m"'
bind '"\ec": " \C-e\C-u$(__fzf_cd__)\e\C-e\er\C-m"'
else
bind '"\C-x\C-e": shell-expand-line'
bind '"\C-x\C-r": redraw-current-line'
@@ -55,18 +63,18 @@ else
# 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"'
bind '"\C-t": "\e$a \eddi$(__fzf_select_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\exa "'
bind '"\C-t": "\e$a \eddi$(__fzf_select__)\C-x\C-e\e0Px$a \C-x\C-r\exa "'
fi
bind -m vi-command '"\C-t": "i\C-t"'
# CTRL-R - Paste the selected command from history into the command line
bind '"\C-r": "\eddi$(HISTTIMEFORMAT= history | fzf +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r | sed \"s/ *[0-9]* *//\")\C-x\C-e\e$a\C-x\C-r"'
bind '"\C-r": "\eddi$(__fzf_history__)\C-x\C-e\e$a\C-x\C-r"'
bind -m vi-command '"\C-r": "i\C-r"'
# ALT-C - cd into the selected directory
bind '"\ec": "\eddi$(__fcd)\C-x\C-e\C-x\C-r\C-m"'
bind '"\ec": "\eddi$(__fzf_cd__)\C-x\C-e\C-x\C-r\C-m"'
bind -m vi-command '"\ec": "i\ec"'
fi

View File

@@ -7,18 +7,6 @@ function fzf_key_bindings
set -g TMPDIR /tmp
end
function __fzf_list
command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | sed 1d | cut -b3-
end
function __fzf_list_dir
command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) \
-prune -o -type d -print 2> /dev/null | sed 1d | cut -b3-
end
function __fzf_escape
while read item
echo -n (echo -n "$item" | sed -E 's/([ "$~'\''([{<>})])/\\\\\\1/g')' '
@@ -26,25 +14,17 @@ function fzf_key_bindings
end
function __fzf_ctrl_t
if [ -n "$TMUX_PANE" -a "$FZF_TMUX" != "0" ]
# FIXME need to handle directory with double-quotes
tmux split-window (__fzf_tmux_height) "cd \"$PWD\";fish -c 'fzf_key_bindings; __fzf_ctrl_t_tmux \\$TMUX_PANE'"
else
__fzf_list | fzf -m > $TMPDIR/fzf.result
and commandline -i (cat $TMPDIR/fzf.result | __fzf_escape)
commandline -f repaint
rm -f $TMPDIR/fzf.result
end
end
function __fzf_ctrl_t_tmux
__fzf_list | fzf -m > $TMPDIR/fzf.result
and tmux send-keys -t $argv[1] (cat $TMPDIR/fzf.result | __fzf_escape)
command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | sed 1d | cut -b3- | eval (__fzfcmd) -m > $TMPDIR/fzf.result
and commandline -i (cat $TMPDIR/fzf.result | __fzf_escape)
commandline -f repaint
rm -f $TMPDIR/fzf.result
end
function __fzf_ctrl_r
history | fzf +s +m --tiebreak=index --toggle-sort=ctrl-r > $TMPDIR/fzf.result
history | eval (__fzfcmd) +s +m --tiebreak=index --toggle-sort=ctrl-r > $TMPDIR/fzf.result
and commandline (cat $TMPDIR/fzf.result)
commandline -f repaint
rm -f $TMPDIR/fzf.result
@@ -52,25 +32,26 @@ function fzf_key_bindings
function __fzf_alt_c
# Fish hangs if the command before pipe redirects (2> /dev/null)
__fzf_list_dir | fzf +m > $TMPDIR/fzf.result
command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) \
-prune -o -type d -print 2> /dev/null | sed 1d | cut -b3- | eval (__fzfcmd) +m > $TMPDIR/fzf.result
[ (cat $TMPDIR/fzf.result | wc -l) -gt 0 ]
and cd (cat $TMPDIR/fzf.result)
commandline -f repaint
rm -f $TMPDIR/fzf.result
end
function __fzf_tmux_height
if set -q FZF_TMUX_HEIGHT
set height $FZF_TMUX_HEIGHT
function __fzfcmd
set -q FZF_TMUX; or set FZF_TMUX 1
if [ $FZF_TMUX -eq 1 ]
if set -q FZF_TMUX_HEIGHT
echo "fzf-tmux -d$FZF_TMUX_HEIGHT"
else
echo "fzf-tmux -d40%"
end
else
set height 40%
echo "fzf"
end
if echo $height | \grep -q -E '%$'
echo "-p "(echo $height | sed 's/%$//')
else
echo "-l $height"
end
set -e height
end
bind \ct '__fzf_ctrl_t'

View File

@@ -5,38 +5,29 @@ __fsel() {
command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | sed 1d | cut -b3- | fzf -m | while read item; do
-o -type l -print 2> /dev/null | sed 1d | cut -b3- | $(__fzfcmd) -m | while read item; do
printf '%q ' "$item"
done
echo
}
__fzfcmd() {
[ ${FZF_TMUX:-1} -eq 1 ] && echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
}
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 "cd $(printf %q "$PWD");zsh -c 'source ~/.fzf.zsh; tmux send-keys -t $TMUX_PANE \"\$(__fsel)\"'"
}
else
fzf-file-widget() {
LBUFFER="${LBUFFER}$(__fsel)"
zle redisplay
}
fi
fzf-file-widget() {
LBUFFER="${LBUFFER}$(__fsel)"
zle redisplay
}
zle -N fzf-file-widget
bindkey '^T' fzf-file-widget
# ALT-C - cd into the selected directory
fzf-cd-widget() {
cd "${$(command find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
-o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m):-.}"
-o -type d -print 2> /dev/null | sed 1d | cut -b3- | $(__fzfcmd) +m):-.}"
zle reset-prompt
}
zle -N fzf-cd-widget
@@ -45,7 +36,7 @@ bindkey '\ec' fzf-cd-widget
# CTRL-R - Paste the selected command from history into the command line
fzf-history-widget() {
local selected
if selected=$(fc -l 1 | fzf +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r -q "$LBUFFER"); then
if selected=$(fc -l 1 | $(__fzfcmd) +s --tac +m -n2..,.. --tiebreak=index --toggle-sort=ctrl-r -q "$LBUFFER"); then
num=$(echo "$selected" | head -1 | awk '{print $1}' | sed 's/[^0-9]//g')
LBUFFER=!$num
zle expand-history

View File

@@ -42,10 +42,8 @@ func FuzzyMatch(caseSensitive bool, runes *[]rune, pattern []rune) (int, int) {
// compiler as of now does not inline non-leaf functions.)
if char >= 'A' && char <= 'Z' {
char += 32
(*runes)[index] = char
} else if char > unicode.MaxASCII {
char = unicode.To(unicode.LowerCase, char)
(*runes)[index] = char
}
}
if char == pattern[pidx] {
@@ -63,6 +61,13 @@ func FuzzyMatch(caseSensitive bool, runes *[]rune, pattern []rune) (int, int) {
pidx--
for index := eidx - 1; index >= sidx; index-- {
char := (*runes)[index]
if !caseSensitive {
if char >= 'A' && char <= 'Z' {
char += 32
} else if char > unicode.MaxASCII {
char = unicode.To(unicode.LowerCase, char)
}
}
if char == pattern[pidx] {
if pidx--; pidx < 0 {
sidx = index

View File

@@ -8,7 +8,7 @@ import (
const (
// Current version
Version = "0.9.10"
Version = "0.9.11"
// Core
coordinatorDelayMax time.Duration = 100 * time.Millisecond

View File

@@ -40,6 +40,7 @@ const usage = `usage: fzf [options]
--black Use black background
--reverse Reverse orientation
--no-hscroll Disable horizontal scroll
--inline-info Display finder info inline with the query
--prompt=STR Input prompt (default: '> ')
Scripting
@@ -105,6 +106,7 @@ type Options struct {
Black bool
Reverse bool
Hscroll bool
InlineInfo bool
Prompt string
Query string
Select1 bool
@@ -141,6 +143,7 @@ func defaultOptions() *Options {
Black: false,
Reverse: false,
Hscroll: true,
InlineInfo: false,
Prompt: "> ",
Query: "",
Select1: false,
@@ -364,6 +367,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Hscroll = true
case "--no-hscroll":
opts.Hscroll = false
case "--inline-info":
opts.InlineInfo = true
case "--no-inline-info":
opts.InlineInfo = false
case "-1", "--select-1":
opts.Select1 = true
case "+1", "--no-select-1":

View File

@@ -4,7 +4,6 @@ import (
"regexp"
"sort"
"strings"
"unicode"
"github.com/junegunn/fzf/src/algo"
)
@@ -28,10 +27,11 @@ const (
)
type term struct {
typ termType
inv bool
text []rune
origText []rune
typ termType
inv bool
text []rune
caseSensitive bool
origText []rune
}
// Pattern represents search pattern
@@ -88,36 +88,27 @@ func BuildPattern(mode Mode, caseMode Case,
caseSensitive, hasInvTerm := true, false
terms := []term{}
switch caseMode {
case CaseSmart:
hasUppercase := false
for _, r := range runes {
if unicode.IsUpper(r) {
hasUppercase = true
break
}
}
if !hasUppercase {
runes, caseSensitive = []rune(strings.ToLower(asString)), false
}
case CaseIgnore:
runes, caseSensitive = []rune(strings.ToLower(asString)), false
}
switch mode {
case ModeExtended, ModeExtendedExact:
terms = parseTerms(mode, string(runes))
terms = parseTerms(mode, caseMode, asString)
for _, term := range terms {
if term.inv {
hasInvTerm = true
}
}
default:
lowerString := strings.ToLower(asString)
caseSensitive = caseMode == CaseRespect ||
caseMode == CaseSmart && lowerString != asString
if !caseSensitive {
asString = lowerString
}
}
ptr := &Pattern{
mode: mode,
caseSensitive: caseSensitive,
text: runes,
text: []rune(asString),
terms: terms,
hasInvTerm: hasInvTerm,
nth: nth,
@@ -133,11 +124,17 @@ func BuildPattern(mode Mode, caseMode Case,
return ptr
}
func parseTerms(mode Mode, str string) []term {
func parseTerms(mode Mode, caseMode Case, str string) []term {
tokens := _splitRegex.Split(str, -1)
terms := []term{}
for _, token := range tokens {
typ, inv, text := termFuzzy, false, token
lowerText := strings.ToLower(text)
caseSensitive := caseMode == CaseRespect ||
caseMode == CaseSmart && text != lowerText
if !caseSensitive {
text = lowerText
}
origText := []rune(text)
if mode == ModeExtendedExact {
typ = termExact
@@ -163,10 +160,11 @@ func parseTerms(mode Mode, str string) []term {
if len(text) > 0 {
terms = append(terms, term{
typ: typ,
inv: inv,
text: []rune(text),
origText: origText})
typ: typ,
inv: inv,
text: []rune(text),
caseSensitive: caseSensitive,
origText: origText})
}
}
return terms
@@ -280,7 +278,7 @@ func dupItem(item *Item, offsets []Offset) *Item {
func (p *Pattern) fuzzyMatch(item *Item) (int, int) {
input := p.prepareInput(item)
return p.iter(algo.FuzzyMatch, input, p.text)
return p.iter(algo.FuzzyMatch, input, p.caseSensitive, p.text)
}
func (p *Pattern) extendedMatch(item *Item) []Offset {
@@ -288,7 +286,7 @@ func (p *Pattern) extendedMatch(item *Item) []Offset {
offsets := []Offset{}
for _, term := range p.terms {
pfun := p.procFun[term.typ]
if sidx, eidx := p.iter(pfun, input, term.text); sidx >= 0 {
if sidx, eidx := p.iter(pfun, input, term.caseSensitive, term.text); sidx >= 0 {
if term.inv {
break
}
@@ -319,10 +317,10 @@ func (p *Pattern) prepareInput(item *Item) *[]Token {
}
func (p *Pattern) iter(pfun func(bool, *[]rune, []rune) (int, int),
tokens *[]Token, pattern []rune) (int, int) {
tokens *[]Token, caseSensitive bool, pattern []rune) (int, int) {
for _, part := range *tokens {
prefixLength := part.prefixLength
if sidx, eidx := pfun(p.caseSensitive, part.text, pattern); sidx >= 0 {
if sidx, eidx := pfun(caseSensitive, part.text, pattern); sidx >= 0 {
return sidx + prefixLength, eidx + prefixLength
}
}

View File

@@ -7,7 +7,7 @@ import (
)
func TestParseTermsExtended(t *testing.T) {
terms := parseTerms(ModeExtended,
terms := parseTerms(ModeExtended, CaseSmart,
"aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$")
if len(terms) != 8 ||
terms[0].typ != termFuzzy || terms[0].inv ||
@@ -31,7 +31,7 @@ func TestParseTermsExtended(t *testing.T) {
}
func TestParseTermsExtendedExact(t *testing.T) {
terms := parseTerms(ModeExtendedExact,
terms := parseTerms(ModeExtendedExact, CaseSmart,
"aaa 'bbb ^ccc ddd$ !eee !'fff !^ggg !hhh$")
if len(terms) != 8 ||
terms[0].typ != termExact || terms[0].inv || len(terms[0].text) != 3 ||
@@ -47,7 +47,7 @@ func TestParseTermsExtendedExact(t *testing.T) {
}
func TestParseTermsEmpty(t *testing.T) {
terms := parseTerms(ModeExtended, "' $ ^ !' !^ !$")
terms := parseTerms(ModeExtended, CaseSmart, "' $ ^ !' !^ !$")
if len(terms) != 0 {
t.Errorf("%s", terms)
}

View File

@@ -20,6 +20,7 @@ import (
// Terminal represents terminal input/output
type Terminal struct {
inlineInfo bool
prompt string
reverse bool
hscroll bool
@@ -83,6 +84,7 @@ const (
func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
input := []rune(opts.Query)
return &Terminal{
inlineInfo: opts.InlineInfo,
prompt: opts.Prompt,
reverse: opts.Reverse,
hscroll: opts.Hscroll,
@@ -229,14 +231,23 @@ func (t *Terminal) printPrompt() {
}
func (t *Terminal) printInfo() {
t.move(1, 0, true)
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
C.CPrint(C.ColSpinner, true, _spinner[idx])
if t.inlineInfo {
t.move(0, len(t.prompt)+displayWidth(t.input)+1, true)
if t.reading {
C.CPrint(C.ColSpinner, true, " < ")
} else {
C.CPrint(C.ColPrompt, true, " < ")
}
} else {
t.move(1, 0, true)
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
C.CPrint(C.ColSpinner, true, _spinner[idx])
}
t.move(1, 2, false)
}
t.move(1, 2, false)
output := fmt.Sprintf("%d/%d", t.merger.Length(), t.count)
if t.toggleSort > 0 {
if t.sort {
@@ -257,10 +268,16 @@ func (t *Terminal) printInfo() {
func (t *Terminal) printList() {
t.constrain()
maxy := maxItems()
maxy := t.maxItems()
count := t.merger.Length() - t.offset
for i := 0; i < maxy; i++ {
t.move(i+2, 0, true)
var line int
if t.inlineInfo {
line = i + 1
} else {
line = i + 2
}
t.move(line, 0, true)
if i < count {
t.printItem(t.merger.Get(i+t.offset), i == t.cy-t.offset)
}
@@ -515,6 +532,9 @@ func (t *Terminal) Loop() {
switch req {
case reqPrompt:
t.printPrompt()
if t.inlineInfo {
t.printInfo()
}
case reqInfo:
t.printInfo()
case reqList:
@@ -659,10 +679,10 @@ func (t *Terminal) Loop() {
case C.Del:
t.delChar()
case C.PgUp:
t.vmove(maxItems() - 1)
t.vmove(t.maxItems() - 1)
req(reqList)
case C.PgDn:
t.vmove(-(maxItems() - 1))
t.vmove(-(t.maxItems() - 1))
req(reqList)
case C.AltB:
t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
@@ -685,6 +705,10 @@ func (t *Terminal) Loop() {
if !t.reverse {
my = C.MaxY() - my - 1
}
min := 2
if t.inlineInfo {
min = 1
}
if me.S != 0 {
// Scroll
if t.merger.Length() > 0 {
@@ -696,8 +720,8 @@ func (t *Terminal) Loop() {
}
} else if me.Double {
// Double-click
if my >= 2 {
if t.vset(my-2) && t.cy < t.merger.Length() {
if my >= min {
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
req(reqClose)
}
}
@@ -705,9 +729,9 @@ func (t *Terminal) Loop() {
if my == 0 && mx >= 0 {
// Prompt
t.cx = mx
} else if my >= 2 {
} else if my >= min {
// List
if t.vset(t.offset+my-2) && t.multi && me.Mod {
if t.vset(t.offset+my-min) && t.multi && me.Mod {
toggle()
}
req(reqList)
@@ -728,7 +752,7 @@ func (t *Terminal) Loop() {
func (t *Terminal) constrain() {
count := t.merger.Length()
height := C.MaxY() - 2
height := t.maxItems()
diffpos := t.cy - t.offset
t.cy = util.Constrain(t.cy, 0, count-1)
@@ -761,6 +785,10 @@ func (t *Terminal) vset(o int) bool {
return t.cy == o
}
func maxItems() int {
return C.MaxY() - 2
func (t *Terminal) maxItems() int {
if t.inlineInfo {
return C.MaxY() - 1
} else {
return C.MaxY() - 2
}
}

View File

@@ -525,6 +525,20 @@ class TestGoFZF < TestBase
File.unlink tempname
end
def test_invalid_cache
tmux.send_keys "(echo d; echo D; echo x) | #{fzf '-q d'}", :Enter
tmux.until { |lines| lines[-2].include? '2/3' }
tmux.send_keys :BSpace
tmux.until { |lines| lines[-2].include? '3/3' }
tmux.send_keys :D
tmux.until { |lines| lines[-2].include? '1/3' }
tmux.send_keys :Enter
end
def test_smart_case_for_each_term
assert_equal 1, `echo Foo bar | #{FZF} -x -f "foo Fbar" | wc -l`.to_i
end
private
def writelines path, lines, timeout = 10
File.open(path, 'w') do |f|
@@ -568,11 +582,11 @@ module TestShell
def test_alt_c
tmux.prepare
tmux.send_keys :Escape, :c
lines = tmux.until { |lines| lines[-1].start_with? '>' }
tmux.send_keys :Escape, :c, pane: 0
lines = tmux.until(pane: 1) { |lines| lines[-1].start_with? '>' }
expected = lines[-3][2..-1]
p expected
tmux.send_keys :Enter
tmux.send_keys :Enter, pane: 1
tmux.prepare
tmux.send_keys :pwd, :Enter
tmux.until { |lines| p lines; lines[-1].end_with?(expected) }
@@ -585,11 +599,11 @@ module TestShell
tmux.send_keys 'echo 3d', :Enter; tmux.prepare
tmux.send_keys 'echo 3rd', :Enter; tmux.prepare
tmux.send_keys 'echo 4th', :Enter; tmux.prepare
tmux.send_keys 'C-r'
tmux.until { |lines| lines[-1].start_with? '>' }
tmux.send_keys '3d'
tmux.until { |lines| lines[-3].end_with? 'echo 3rd' } # --no-sort
tmux.send_keys :Enter
tmux.send_keys 'C-r', pane: 0
tmux.until(pane: 1) { |lines| lines[-1].start_with? '>' }
tmux.send_keys '3d', pane: 1
tmux.until(pane: 1) { |lines| lines[-3].end_with? 'echo 3rd' } # --no-sort
tmux.send_keys :Enter, pane: 1
tmux.until { |lines| lines[-1] == 'echo 3rd' }
tmux.send_keys :Enter
tmux.until { |lines| lines[-1] == '3rd' }