Implement smart-case match (#12)

This commit is contained in:
Junegunn Choi 2013-12-20 15:30:48 +09:00
parent b30f21e074
commit 159dd7f069
3 changed files with 61 additions and 10 deletions

View File

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

20
fzf
View File

@ -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
@ -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
@ -830,16 +836,16 @@ class FZF
when ''
nil
when /^\^(.*)\$$/
Regexp.new('^' << sanitize(Regexp.escape($1)) << '$', rxflag)
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

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