m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-12 13:23:48 -05:00

Compare commits

...

11 Commits
0.5.1 ... 0.6.0

Author SHA1 Message Date
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
6 changed files with 195 additions and 45 deletions

View File

@@ -16,8 +16,6 @@ fzf requires Ruby (>= 1.8.5).
Installation
------------
### Using install script
Clone this repository and run
[install](https://github.com/junegunn/fzf/blob/master/install) script.
@@ -29,29 +27,10 @@ git clone https://github.com/junegunn/fzf.git ~/.fzf
The script will generate `~/.fzf.bash` and `~/.fzf.zsh` and update your
`.bashrc` and `.zshrc` to load them.
### Manual installation
Or you can just 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
```
### 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.
### Install as Vim plugin
You can use any Vim plugin manager to install fzf for Vim. If you don't use one,
@@ -78,6 +57,7 @@ usage: fzf [options]
-q, --query=STR Initial query
-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-insensitive match (default: smart-case match)
+i Case-sensitive match
+c, --no-color Disable colors
```
@@ -173,8 +153,8 @@ 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
@@ -189,7 +169,7 @@ fd() {
# fda - including hidden directories
fda() {
DIR=$(find ${1:-*} -type d 2> /dev/null | fzf) && cd "$DIR"
DIR=$(find ${1:-.} -type d 2> /dev/null | fzf) && cd "$DIR"
}
# fh - repeat history
@@ -203,8 +183,16 @@ fkill() {
}
```
bash key bindings
-----------------
Key bindings for command line
-----------------------------
The install script will add the following key bindings to your configuration
files.
### bash
- `CTRL-T` - Paste the selected file path(s) into the command line
- `CTRL-R` - Paste the selected command from history into the command line
```sh
# Required to refresh the prompt after fzf
@@ -212,7 +200,9 @@ bind '"\er": redraw-current-line'
# CTRL-T - Paste the selected file path into the command line
fsel() {
find ${1:-*} | fzf -m | while read item; do
find * -path '*/\.*' -prune \
-o -type f -print \
-o -type l -print 2> /dev/null | fzf -m | while read item; do
printf '%q ' "$item"
done
echo
@@ -223,8 +213,11 @@ 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"'
bind '"\C-r": " \C-e\C-u$(history | fzf +s | sed \"s/ *[0-9]* *//\")\e\C-e\er"'
```
zsh widgets
-----------
### 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
```sh
# CTRL-T - Paste the selected file path(s) into the command line
@@ -262,8 +255,8 @@ zle -N fzf-history-widget
bindkey '^R' fzf-history-widget
```
Auto-completion (experimental)
------------------------------
Auto-completion
---------------
Disclaimer: *Auto-completion feature is currently experimental, it can change
over time*
@@ -367,6 +360,20 @@ 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.
### 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_SORT`.
```sh
export FZF_DEFAULT_SORT=10000
```
License
-------

26
fzf
View File

@@ -10,7 +10,7 @@
# URL: https://github.com/junegunn/fzf
# Author: Junegunn Choi
# License: MIT
# Last update: December 5, 2013
# Last update: December 20, 2013
#
# Copyright (c) 2013 Junegunn Choi
#
@@ -67,7 +67,7 @@ class FZF
end
def initialize argv, source = $stdin
@rxflag = Regexp::IGNORECASE
@rxflag = nil
@sort = ENV.fetch('FZF_DEFAULT_SORT', 1000).to_i
@color = true
@multi = false
@@ -79,6 +79,7 @@ class FZF
when '-h', '--help' then usage 0
when '-m', '--multi' then @multi = true
when '-x', '--extended' then @xmode = true
when '-i' then @rxflag = Regexp::IGNORECASE
when '+i' then @rxflag = 0
when '+s', '--no-sort' then @sort = nil
when '+c', '--no-color' then @color = false
@@ -136,6 +137,7 @@ class FZF
-q, --query=STR Initial query
-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-insensitive match (default: smart-case match)
+i Case-sensitive match
+c, --no-color Disable colors]
exit x
@@ -347,7 +349,7 @@ class FZF
tokens << [line[b...e], true]
index = e
end
tokens << [line[index..-1], false]
tokens << [line[index..-1], false] if index < line.length
tokens.reject { |pair| pair.first.empty? }
end
@@ -706,7 +708,7 @@ class FZF
actions[ctrl(:h)] = actions[127]
actions[ctrl(:n)] = actions[ctrl(:j)]
actions[ctrl(:p)] = actions[ctrl(:k)]
actions[ctrl(:g)] = actions[ctrl(:c)] = actions[:esc]
actions[ctrl(:q)] = actions[ctrl(:g)] = actions[ctrl(:c)] = actions[:esc]
actions[:stab] = actions[9]
emit(:key) { [@query.get, cursor] } unless @query.empty?
@@ -770,14 +772,18 @@ class FZF
q.empty?
end
def rxflag_for q
@rxflag || (q =~ /[A-Z]/ ? 0 : Regexp::IGNORECASE)
end
def fuzzy_regex q
@regexp[q] ||= begin
q = q.downcase if @rxflag != 0
q = q.downcase if @rxflag == Regexp::IGNORECASE
Regexp.new(query_chars(q).inject('') { |sum, e|
e = Regexp.escape e
sum << (e.length > 1 ? "(?:#{e}).*?" : # FIXME: not equivalent
"#{e}[^#{e}]*?")
}, @rxflag)
}, rxflag_for(q))
end
end
@@ -829,15 +835,17 @@ class FZF
case w
when ''
nil
when /^\^(.*)\$$/
Regexp.new('^' << sanitize(Regexp.escape($1)) << '$', rxflag_for(w))
when /^'/
w.length > 1 ?
Regexp.new(sanitize(Regexp.escape(w[1..-1])), rxflag) : nil
Regexp.new(sanitize(Regexp.escape(w[1..-1])), rxflag_for(w)) : nil
when /^\^/
w.length > 1 ?
Regexp.new('^' << sanitize(Regexp.escape(w[1..-1])), rxflag) : nil
Regexp.new('^' << sanitize(Regexp.escape(w[1..-1])), rxflag_for(w)) : nil
when /\$$/
w.length > 1 ?
Regexp.new(sanitize(Regexp.escape(w[0..-2])) << '$', rxflag) : nil
Regexp.new(sanitize(Regexp.escape(w[0..-2])) << '$', rxflag_for(w)) : nil
else
fuzzy_regex w
end, invert ]

View File

@@ -100,7 +100,7 @@ _fzf_host_completion() {
local cur prev selected
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
[ "$cur" = '-l' -o "$prev" = '-l' ] && return 1
[[ "$cur" =~ ^- || "$prev" =~ ^- ]] && return 1
tput sc
selected=$(grep -v '^\s*\(#\|$\)' /etc/hosts | awk '{print $2}' | sort -u | fzf $FZF_COMPLETION_OPTS -q "$cur")

View File

@@ -1,7 +1,7 @@
# coding: utf-8
Gem::Specification.new do |spec|
spec.name = 'fzf'
spec.version = '0.5.1'
spec.version = '0.6.0'
spec.authors = ['Junegunn Choi']
spec.email = ['junegunn.c@gmail.com']
spec.description = %q{Fuzzy finder for your shell}

77
install
View File

@@ -38,6 +38,12 @@ 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 ... "
@@ -49,14 +55,85 @@ for shell in bash zsh; do
fi
cat > $src << EOF
# Setup fzf function
# ------------------
unalias fzf 2> /dev/null
fzf() {
$fzf_cmd "\$@"
}
export -f fzf > /dev/null
# Auto-completion
# ---------------
$fzf_completion
EOF
if [ $key_bindings -eq 0 ]; then
if [ $shell = bash ]; then
cat >> $src << "EOF"
# Key bindings
# ------------
# Required to refresh the prompt after fzf
bind '"\er": redraw-current-line'
# CTRL-T - Paste the selected file path into the command line
fsel() {
find * -path '*/\.*' -prune \
-o -type f -print \
-o -type l -print 2> /dev/null | fzf -m | while read item; do
printf '%q ' "$item"
done
echo
}
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"'
# 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"'
EOF
else
cat >> $src << "EOF"
# Key bindings
# ------------
# CTRL-T - Paste the selected file path(s) 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
# 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
# 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
EOF
fi
fi
echo "OK"
done

View File

@@ -9,10 +9,10 @@ load 'fzf'
class TestFZF < MiniTest::Unit::TestCase
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
begin
ENV['FZF_DEFAULT_SORT'] = '1500'
@@ -152,15 +152,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[
@@ -197,6 +241,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
@@ -332,5 +381,14 @@ 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
end