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 -q, --query=STR Initial query
-s, --sort=MAX Maximum number of matched items to sort. Default: 1000 -s, --sort=MAX Maximum number of matched items to sort. Default: 1000
+s, --no-sort Do not sort the result. Keep the sequence unchanged. +s, --no-sort Do not sort the result. Keep the sequence unchanged.
-i Case-insensitive match (default: smart-case match)
+i Case-sensitive match +i Case-sensitive match
+c, --no-color Disable colors +c, --no-color Disable colors
``` ```

20
fzf
View File

@@ -67,7 +67,7 @@ class FZF
end end
def initialize argv, source = $stdin def initialize argv, source = $stdin
@rxflag = Regexp::IGNORECASE @rxflag = nil
@sort = ENV.fetch('FZF_DEFAULT_SORT', 1000).to_i @sort = ENV.fetch('FZF_DEFAULT_SORT', 1000).to_i
@color = true @color = true
@multi = false @multi = false
@@ -79,6 +79,7 @@ class FZF
when '-h', '--help' then usage 0 when '-h', '--help' then usage 0
when '-m', '--multi' then @multi = true when '-m', '--multi' then @multi = true
when '-x', '--extended' then @xmode = true when '-x', '--extended' then @xmode = true
when '-i' then @rxflag = Regexp::IGNORECASE
when '+i' then @rxflag = 0 when '+i' then @rxflag = 0
when '+s', '--no-sort' then @sort = nil when '+s', '--no-sort' then @sort = nil
when '+c', '--no-color' then @color = false when '+c', '--no-color' then @color = false
@@ -136,6 +137,7 @@ class FZF
-q, --query=STR Initial query -q, --query=STR Initial query
-s, --sort=MAX Maximum number of matched items to sort. Default: 1000 -s, --sort=MAX Maximum number of matched items to sort. Default: 1000
+s, --no-sort Do not sort the result. Keep the sequence unchanged. +s, --no-sort Do not sort the result. Keep the sequence unchanged.
-i Case-insensitive match (default: smart-case match)
+i Case-sensitive match +i Case-sensitive match
+c, --no-color Disable colors] +c, --no-color Disable colors]
exit x exit x
@@ -770,14 +772,18 @@ class FZF
q.empty? q.empty?
end end
def rxflag_for q
@rxflag || (q =~ /[A-Z]/ ? 0 : Regexp::IGNORECASE)
end
def fuzzy_regex q def fuzzy_regex q
@regexp[q] ||= begin @regexp[q] ||= begin
q = q.downcase if @rxflag != 0 q = q.downcase if @rxflag == Regexp::IGNORECASE
Regexp.new(query_chars(q).inject('') { |sum, e| Regexp.new(query_chars(q).inject('') { |sum, e|
e = Regexp.escape e e = Regexp.escape e
sum << (e.length > 1 ? "(?:#{e}).*?" : # FIXME: not equivalent sum << (e.length > 1 ? "(?:#{e}).*?" : # FIXME: not equivalent
"#{e}[^#{e}]*?") "#{e}[^#{e}]*?")
}, @rxflag) }, rxflag_for(q))
end end
end end
@@ -830,16 +836,16 @@ class FZF
when '' when ''
nil nil
when /^\^(.*)\$$/ when /^\^(.*)\$$/
Regexp.new('^' << sanitize(Regexp.escape($1)) << '$', rxflag) Regexp.new('^' << sanitize(Regexp.escape($1)) << '$', rxflag_for(w))
when /^'/ when /^'/
w.length > 1 ? 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 /^\^/ when /^\^/
w.length > 1 ? 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 /\$$/ when /\$$/
w.length > 1 ? 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 else
fuzzy_regex w fuzzy_regex w
end, invert ] end, invert ]

View File

@@ -12,7 +12,7 @@ class TestFZF < MiniTest::Unit::TestCase
assert_equal 1000, fzf.sort assert_equal 1000, fzf.sort
assert_equal false, fzf.multi assert_equal false, fzf.multi
assert_equal true, fzf.color assert_equal true, fzf.color
assert_equal Regexp::IGNORECASE, fzf.rxflag assert_equal nil, fzf.rxflag
begin begin
ENV['FZF_DEFAULT_SORT'] = '1500' ENV['FZF_DEFAULT_SORT'] = '1500'
@@ -152,15 +152,59 @@ class TestFZF < MiniTest::Unit::TestCase
# TODO : partial_cache # TODO : partial_cache
end 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 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]]]], assert_equal [['Fruit', [[0, 5]]]],
FZF::FuzzyMatcher.new(0).match(%w[Fruit Grapefruit], 'Fruit', '', '').sort FZF::FuzzyMatcher.new(0).match(%w[Fruit Grapefruit], 'Fruit', '', '').sort
# Case-insensitive match (+i)
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]], assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
FZF::FuzzyMatcher.new(Regexp::IGNORECASE). FZF::FuzzyMatcher.new(Regexp::IGNORECASE).
match(%w[Fruit Grapefruit], 'Fruit', '', '').sort match(%w[Fruit Grapefruit], 'Fruit', '', '').sort
end 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 def test_extended_fuzzy_matcher
matcher = FZF::ExtendedFuzzyMatcher.new Regexp::IGNORECASE matcher = FZF::ExtendedFuzzyMatcher.new Regexp::IGNORECASE
list = %w[ list = %w[