Fix ranking when multiple regions overlap

e.g.
  Match region #1: [-----------]
  Match region #2:       [---]
  Match region #3:         [------]
This commit is contained in:
Junegunn Choi 2014-04-03 14:51:01 +09:00
parent a9056ce90c
commit fa212efe5f
2 changed files with 51 additions and 33 deletions

40
fzf
View File

@ -98,7 +98,7 @@ class FZF
end end
while o = argv.shift while o = argv.shift
case o case o
when '--version' then version when '--version' then FZF.version
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 '+m', '--no-multi' then @multi = false when '+m', '--no-multi' then @multi = false
@ -225,7 +225,7 @@ class FZF
def filter_list list def filter_list list
matches = matcher.match(list, @filter, '', '') matches = matcher.match(list, @filter, '', '')
if @sort && matches.length <= @sort if @sort && matches.length <= @sort
matches = sort_by_rank(matches) matches = FZF.sort(matches)
end end
matches.each { |m| puts m.first } matches.each { |m| puts m.first }
end end
@ -239,6 +239,7 @@ class FZF
end end
end end
class << self
def version def version
File.open(__FILE__, 'r') do |f| File.open(__FILE__, 'r') do |f|
f.each_line do |line| f.each_line do |line|
@ -250,6 +251,24 @@ class FZF
end end
end end
def sort list
list.sort_by { |tuple| rank tuple }
end
def rank tuple
line, offsets = tuple
matchlen = 0
pe = 0
offsets.sort.each do |pair|
b, e = pair
b = pe if pe > b
pe = e if e > pe
matchlen += e - b if e > b
end
[matchlen, line.length, line]
end
end
def usage x, message = nil def usage x, message = nil
$stderr.puts message if message $stderr.puts message if message
$stderr.puts %[usage: fzf [options] $stderr.puts %[usage: fzf [options]
@ -519,21 +538,6 @@ class FZF
C.attroff color(:chosen, true) if chosen C.attroff color(:chosen, true) if chosen
end end
def sort_by_rank list
list.sort_by { |tuple|
line, offsets = tuple
matchlen = 0
pe = nil
offsets.sort.each do |pair|
b, e = pair
b = pe if pe && pe > b
pe = e
matchlen += e - b
end
[matchlen, line.length, line]
}
end
AFTER_1_9 = RUBY_VERSION.split('.').map { |e| e.rjust(3, '0') }.join >= '001009' AFTER_1_9 = RUBY_VERSION.split('.').map { |e| e.rjust(3, '0') }.join >= '001009'
if AFTER_1_9 if AFTER_1_9
@ -725,7 +729,7 @@ class FZF
next if skip next if skip
matches = @sort ? found : found.reverse matches = @sort ? found : found.reverse
if !empty && @sort && matches.length <= @sort if !empty && @sort && matches.length <= @sort
matches = sort_by_rank(matches) matches = FZF.sort(matches)
end end
fcache[q] = matches fcache[q] = matches
end end

View File

@ -400,7 +400,7 @@ class TestFZF < MiniTest::Unit::TestCase
["0____1", [[0, 6]]], ["0____1", [[0, 6]]],
["0_____1", [[0, 7]]], ["0_____1", [[0, 7]]],
["0______1", [[0, 8]]]], ["0______1", [[0, 8]]]],
FZF.new([]).sort_by_rank(matcher.match(list, '01', '', ''))) FZF.sort(matcher.match(list, '01', '', '')))
assert_equal( assert_equal(
[["01", [[0, 1], [1, 2]]], [["01", [[0, 1], [1, 2]]],
@ -411,16 +411,19 @@ class TestFZF < MiniTest::Unit::TestCase
["____0_1", [[4, 5], [6, 7]]], ["____0_1", [[4, 5], [6, 7]]],
["0______1", [[0, 1], [7, 8]]], ["0______1", [[0, 1], [7, 8]]],
["___01___", [[3, 4], [4, 5]]]], ["___01___", [[3, 4], [4, 5]]]],
FZF.new([]).sort_by_rank(xmatcher.match(list, '0 1', '', ''))) FZF.sort(xmatcher.match(list, '0 1', '', '')))
assert_equal( assert_equal(
[["_01_", [[1, 3], [0, 4]]], [["_01_", [[1, 3], [0, 4]], [4, 4, "_01_"]],
["0____1", [[0, 6], [1, 3]]], ["___01___", [[3, 5], [0, 2]], [4, 8, "___01___"]],
["0_____1", [[0, 7], [1, 3]]], ["____0_1", [[4, 7], [0, 2]], [5, 7, "____0_1"]],
["0______1", [[0, 8], [1, 3]]], ["0____1", [[0, 6], [1, 3]], [6, 6, "0____1"]],
["___01___", [[3, 5], [0, 2]]], ["0_____1", [[0, 7], [1, 3]], [7, 7, "0_____1"]],
["____0_1", [[4, 7], [0, 2]]]], ["0______1", [[0, 8], [1, 3]], [8, 8, "0______1"]]],
FZF.new([]).sort_by_rank(xmatcher.match(list, '01 __', '', ''))) FZF.sort(xmatcher.match(list, '01 __', '', '')).map { |tuple|
tuple << FZF.rank(tuple)
}
)
end end
def test_extended_exact_mode def test_extended_exact_mode
@ -647,5 +650,16 @@ class TestFZF < MiniTest::Unit::TestCase
$stdout = STDOUT $stdout = STDOUT
end end
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 end