Compare commits
2 Commits
master
...
multi-proc
Author | SHA1 | Date | |
---|---|---|---|
|
c980f9d3ce | ||
|
501af62661 |
226
fzf
226
fzf
@ -7,7 +7,7 @@
|
|||||||
# / __/ / /_/ __/
|
# / __/ / /_/ __/
|
||||||
# /_/ /___/_/ Fuzzy finder for your shell
|
# /_/ /___/_/ Fuzzy finder for your shell
|
||||||
#
|
#
|
||||||
# Version: 0.8.3 (April 3, 2014)
|
# Version: 0.9.0-alpha (April 21, 2014)
|
||||||
#
|
#
|
||||||
# Author: Junegunn Choi
|
# Author: Junegunn Choi
|
||||||
# URL: https://github.com/junegunn/fzf
|
# URL: https://github.com/junegunn/fzf
|
||||||
@ -74,6 +74,35 @@ class FZF
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
def cores?
|
||||||
|
if File.readable? '/proc/cpuinfo'
|
||||||
|
cores = Hash.new { |h, k| h[k] = Set.new }
|
||||||
|
proc_id = nil
|
||||||
|
set = Set.new
|
||||||
|
File.readlines('/proc/cpuinfo').each do |line|
|
||||||
|
case line
|
||||||
|
when /physical id/
|
||||||
|
if proc_id != line
|
||||||
|
cores[line] = set
|
||||||
|
set = Set.new
|
||||||
|
end
|
||||||
|
cores[proc_id = line] = set
|
||||||
|
when /core id/
|
||||||
|
set << line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cores.values.inject(0) { |sum, s| sum + (s.length || 1) }
|
||||||
|
else
|
||||||
|
cores = `sysctl -n hw.physicalcpu 2> /dev/null`.chomp
|
||||||
|
if $?.exitstatus == 0
|
||||||
|
cores.to_i
|
||||||
|
else
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def initialize argv, source = $stdin
|
def initialize argv, source = $stdin
|
||||||
@rxflag = nil
|
@rxflag = nil
|
||||||
@sort = ENV.fetch('FZF_DEFAULT_SORT', 1000).to_i
|
@sort = ENV.fetch('FZF_DEFAULT_SORT', 1000).to_i
|
||||||
@ -88,6 +117,9 @@ class FZF
|
|||||||
@filter = nil
|
@filter = nil
|
||||||
@nth = nil
|
@nth = nil
|
||||||
@delim = nil
|
@delim = nil
|
||||||
|
@pids = []
|
||||||
|
@procs = nil
|
||||||
|
@par_min = 10000
|
||||||
|
|
||||||
argv =
|
argv =
|
||||||
if opts = ENV['FZF_DEFAULT_OPTS']
|
if opts = ENV['FZF_DEFAULT_OPTS']
|
||||||
@ -148,11 +180,28 @@ class FZF
|
|||||||
@sort = $1.to_i
|
@sort = $1.to_i
|
||||||
when '-e', '--extended-exact' then @extended = :exact
|
when '-e', '--extended-exact' then @extended = :exact
|
||||||
when '+e', '--no-extended-exact' then @extended = nil
|
when '+e', '--no-extended-exact' then @extended = nil
|
||||||
|
when '-p', '--parallel'
|
||||||
|
usage 1, 'number of processes required' unless pnum = argv.shift
|
||||||
|
@procs = pnum.to_i
|
||||||
|
when /^-p([1-9][0-9]*)$/, /^--parallel=([1-9][0-9]*)$/
|
||||||
|
@procs = $1.to_i
|
||||||
|
when '--parallel-min'
|
||||||
|
usage 1, 'number required' unless pmin = argv.shift
|
||||||
|
@par_min = pmin.to_i
|
||||||
|
when /^--parallel-min=([1-9][0-9]*)$/
|
||||||
|
@par_min = $1.to_i
|
||||||
else
|
else
|
||||||
usage 1, "illegal option: #{o}"
|
usage 1, "illegal option: #{o}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
require 'msgpack'
|
||||||
|
@procs ||= cores?
|
||||||
|
rescue LoadError
|
||||||
|
@procs = nil
|
||||||
|
end
|
||||||
|
|
||||||
@source = source.clone
|
@source = source.clone
|
||||||
@mtx = Mutex.new
|
@mtx = Mutex.new
|
||||||
@cv = ConditionVariable.new
|
@cv = ConditionVariable.new
|
||||||
@ -223,7 +272,7 @@ class FZF
|
|||||||
end
|
end
|
||||||
|
|
||||||
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 = FZF.sort(matches)
|
matches = FZF.sort(matches)
|
||||||
end
|
end
|
||||||
@ -281,6 +330,10 @@ class FZF
|
|||||||
-n, --nth=[-]N[,..] Comma-separated list of field indexes for limiting
|
-n, --nth=[-]N[,..] Comma-separated list of field indexes for limiting
|
||||||
search scope (positive or negative integers)
|
search scope (positive or negative integers)
|
||||||
-d, --delimiter=STR Field delimiter regex for --nth (default: AWK-style)
|
-d, --delimiter=STR Field delimiter regex for --nth (default: AWK-style)
|
||||||
|
-p, --parallel=N Number of processes for parallel search
|
||||||
|
(default: auto-detect)
|
||||||
|
--parallel-min=N Minimum number of items to start parallel search
|
||||||
|
(default: 10000)
|
||||||
|
|
||||||
Search result
|
Search result
|
||||||
-s, --sort=MAX Maximum number of matched items to sort (default: 1000)
|
-s, --sort=MAX Maximum number of matched items to sort (default: 1000)
|
||||||
@ -667,6 +720,100 @@ class FZF
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cached lists, q, prefix, suffix
|
||||||
|
cnt = 0
|
||||||
|
cached = lists.inject({}) do |sum, l|
|
||||||
|
cached = matcher.cached(l, q, prefix, suffix)
|
||||||
|
cnt += cached ? cached.length : l.length
|
||||||
|
sum[l] = cached
|
||||||
|
sum
|
||||||
|
end
|
||||||
|
[cnt, cached]
|
||||||
|
end
|
||||||
|
|
||||||
|
def search lists, q, cx
|
||||||
|
cache_count, cached = cached(lists, q, q[0, cx], q[cx..-1])
|
||||||
|
if !@procs || @procs <= 1 || lists.empty? || lists.length < @procs || cache_count < @par_min
|
||||||
|
search_sequential lists, q, cached
|
||||||
|
else
|
||||||
|
search_parallel lists, q, cached
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_sequential lists, q, cached
|
||||||
|
progress = 0
|
||||||
|
started_at = Time.now
|
||||||
|
|
||||||
|
found = []
|
||||||
|
skip = false
|
||||||
|
cnt = 0
|
||||||
|
lists.each do |list|
|
||||||
|
cnt += list.length
|
||||||
|
skip = @mtx.synchronize { @events[:key] }
|
||||||
|
break if skip
|
||||||
|
|
||||||
|
if (progress = 100 * cnt / @count.get) < 100 && Time.now - started_at > 0.5
|
||||||
|
render { print_info " (#{progress}%)" }
|
||||||
|
end
|
||||||
|
matches = matcher.match(list, q, cached[list])
|
||||||
|
matcher.cache list, q, matches
|
||||||
|
found.concat matches
|
||||||
|
end
|
||||||
|
return :skip if skip
|
||||||
|
found
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_parallel lists, q, cached
|
||||||
|
list_map = lists.inject({}) { |h, l| h[l.object_id] = l; h }
|
||||||
|
slice_size = lists.length / @procs
|
||||||
|
slices = lists.each_slice(slice_size)
|
||||||
|
|
||||||
|
render { print_info " (#{@procs}x)" }
|
||||||
|
triples = slices.map do |lists|
|
||||||
|
read, write = IO.pipe
|
||||||
|
[fork do
|
||||||
|
read.close
|
||||||
|
running = true
|
||||||
|
Signal.trap('USR1') do
|
||||||
|
running = false
|
||||||
|
end
|
||||||
|
matches = {}
|
||||||
|
lists.each do |list|
|
||||||
|
break unless running
|
||||||
|
matches[list.object_id] = matcher.match(list, q, cached[list])
|
||||||
|
end
|
||||||
|
write << MessagePack.pack(matches)
|
||||||
|
exit! running
|
||||||
|
end, read, write]
|
||||||
|
end
|
||||||
|
|
||||||
|
matches = []
|
||||||
|
@pids = triples.map { |t| t.first }
|
||||||
|
mutex = Mutex.new
|
||||||
|
skip = false
|
||||||
|
triples.map { |pid, read, write|
|
||||||
|
Thread.new do
|
||||||
|
write.close
|
||||||
|
result = read.read
|
||||||
|
_, status = Process.wait2(pid)
|
||||||
|
skip = status.exitstatus != 0
|
||||||
|
MessagePack.unpack(result).each do |list_object_id, data|
|
||||||
|
mutex.synchronize do
|
||||||
|
matches.concat data
|
||||||
|
end
|
||||||
|
matcher.cache list_map[list_object_id], q, data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}.each(&:join)
|
||||||
|
skip ? :skip : matches
|
||||||
|
end
|
||||||
|
|
||||||
|
def signal_children
|
||||||
|
while pid = @pids.pop
|
||||||
|
Process.kill 'USR1', pid rescue nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def start_search &callback
|
def start_search &callback
|
||||||
Thread.new do
|
Thread.new do
|
||||||
lists = []
|
lists = []
|
||||||
@ -704,39 +851,26 @@ class FZF
|
|||||||
|
|
||||||
new_search = events[:key] || events.delete(:new)
|
new_search = events[:key] || events.delete(:new)
|
||||||
user_input = events[:key]
|
user_input = events[:key]
|
||||||
progress = 0
|
|
||||||
started_at = Time.now
|
|
||||||
|
|
||||||
if updated = new_search && !lists.empty?
|
if updated = new_search && !lists.empty?
|
||||||
q, cx = events.delete(:key) || [q, 0]
|
q, cx = events.delete(:key) || [q, 0]
|
||||||
empty = matcher.empty?(q)
|
|
||||||
unless matches = fcache[q]
|
unless matches = fcache[q]
|
||||||
found = []
|
# Simply concats the list
|
||||||
skip = false
|
if matcher.empty?(q)
|
||||||
cnt = 0
|
matches = lists.inject([]) { |cc, l| cc.concat l }
|
||||||
lists.each do |list|
|
else
|
||||||
cnt += list.length
|
matches ||= search(lists, q, cx)
|
||||||
skip = @mtx.synchronize { @events[:key] }
|
next if matches == :skip
|
||||||
break if skip
|
matches = @sort ? matches : matches.reverse
|
||||||
|
if @sort && matches.length <= @sort
|
||||||
if !empty && (progress = 100 * cnt / @count.get) < 100 && Time.now - started_at > 0.5
|
matches = FZF.sort(matches)
|
||||||
render { print_info " (#{progress}%)" }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
found.concat(q.empty? ? list :
|
|
||||||
matcher.match(list, q, q[0, cx], q[cx..-1]))
|
|
||||||
end
|
end
|
||||||
next if skip
|
|
||||||
matches = @sort ? found : found.reverse
|
|
||||||
if !empty && @sort && matches.length <= @sort
|
|
||||||
matches = FZF.sort(matches)
|
|
||||||
end
|
|
||||||
fcache[q] = matches
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Atomic update
|
# Atomic update
|
||||||
@matches.set matches
|
@matches.set fcache[q] = matches
|
||||||
end#new_search
|
end
|
||||||
|
|
||||||
callback = nil if callback &&
|
callback = nil if callback &&
|
||||||
(updated || events[:loaded]) &&
|
(updated || events[:loaded]) &&
|
||||||
@ -1083,7 +1217,10 @@ class FZF
|
|||||||
upd = actions.fetch(key, actions[:default]).call(key)
|
upd = actions.fetch(key, actions[:default]).call(key)
|
||||||
|
|
||||||
# Dispatch key event
|
# Dispatch key event
|
||||||
emit(:key) { [@query.set(input.dup), cursor] } if upd
|
if upd
|
||||||
|
signal_children
|
||||||
|
emit(:key) { [@query.set(input.dup), cursor] }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
@ -1176,9 +1313,7 @@ class FZF
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def match list, q, prefix, suffix
|
def cached list, q, prefix, suffix
|
||||||
regexp = fuzzy_regex q
|
|
||||||
|
|
||||||
cache = @caches[list.object_id]
|
cache = @caches[list.object_id]
|
||||||
prefix_cache = nil
|
prefix_cache = nil
|
||||||
(prefix.length - 1).downto(1) do |len|
|
(prefix.length - 1).downto(1) do |len|
|
||||||
@ -1190,10 +1325,17 @@ class FZF
|
|||||||
break if suffix_cache = cache[suffix[idx..-1]]
|
break if suffix_cache = cache[suffix[idx..-1]]
|
||||||
end unless suffix.empty?
|
end unless suffix.empty?
|
||||||
|
|
||||||
partial_cache = [prefix_cache,
|
[prefix_cache, suffix_cache].compact.sort_by { |e| e.length }.first
|
||||||
suffix_cache].compact.sort_by { |e| e.length }.first
|
end
|
||||||
cache[q] ||= (partial_cache ?
|
|
||||||
partial_cache.map { |e| e.first } : list).map { |line|
|
def cache list, q, data
|
||||||
|
@caches[list.object_id][q] = data
|
||||||
|
end
|
||||||
|
|
||||||
|
def match list, q, partial_cache = nil
|
||||||
|
regexp = fuzzy_regex q
|
||||||
|
|
||||||
|
(partial_cache ? partial_cache.map { |e| e.first } : list).map { |line|
|
||||||
# Ignore errors: e.g. invalid byte sequence in UTF-8
|
# Ignore errors: e.g. invalid byte sequence in UTF-8
|
||||||
md = do_match(line, regexp)
|
md = do_match(line, regexp)
|
||||||
md && [line, [md.offset(0)]]
|
md && [line, [md.offset(0)]]
|
||||||
@ -1249,7 +1391,11 @@ class FZF
|
|||||||
Regexp.new(sanitize(Regexp.escape(w)), rxflag_for(w))
|
Regexp.new(sanitize(Regexp.escape(w)), rxflag_for(w))
|
||||||
end
|
end
|
||||||
|
|
||||||
def match list, q, prefix, suffix
|
def cache list, q, data
|
||||||
|
@caches[list.object_id][Set[parse q]] = data
|
||||||
|
end
|
||||||
|
|
||||||
|
def cached list, q, prefix, suffix
|
||||||
regexps = parse q
|
regexps = parse q
|
||||||
# Look for prefix cache
|
# Look for prefix cache
|
||||||
cache = @caches[list.object_id]
|
cache = @caches[list.object_id]
|
||||||
@ -1258,10 +1404,12 @@ class FZF
|
|||||||
(prefix.length - 1).downto(1) do |len|
|
(prefix.length - 1).downto(1) do |len|
|
||||||
break if prefix_cache = cache[Set[@regexps[prefix[0, len]]]]
|
break if prefix_cache = cache[Set[@regexps[prefix[0, len]]]]
|
||||||
end
|
end
|
||||||
|
prefix_cache
|
||||||
|
end
|
||||||
|
|
||||||
cache[Set[regexps]] ||= (prefix_cache ?
|
def match list, q, partial_cache = nil
|
||||||
prefix_cache.map { |e| e.first } :
|
regexps = parse q
|
||||||
list).map { |line|
|
(partial_cache ? partial_cache.map { |e| e.first } : list).map { |line|
|
||||||
offsets = []
|
offsets = []
|
||||||
regexps.all? { |pair|
|
regexps.all? { |pair|
|
||||||
regexp, invert = pair
|
regexp, invert = pair
|
||||||
|
113
test/test_fzf.rb
113
test/test_fzf.rb
@ -220,7 +220,10 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
["juiceless", [[0, 1]]],
|
["juiceless", [[0, 1]]],
|
||||||
["juicily", [[0, 1]]],
|
["juicily", [[0, 1]]],
|
||||||
["juiciness", [[0, 1]]],
|
["juiciness", [[0, 1]]],
|
||||||
["juicy", [[0, 1]]]], matcher.match(list, 'j', '', '').sort)
|
["juicy", [[0, 1]]]], matcher.match(list, 'j').sort)
|
||||||
|
assert matcher.caches.empty?
|
||||||
|
matcher.cache list, 'j', matcher.match(list, 'j')
|
||||||
|
|
||||||
assert !matcher.caches.empty?
|
assert !matcher.caches.empty?
|
||||||
assert_equal [list.object_id], matcher.caches.keys
|
assert_equal [list.object_id], matcher.caches.keys
|
||||||
assert_equal 1, matcher.caches[list.object_id].length
|
assert_equal 1, matcher.caches[list.object_id].length
|
||||||
@ -228,11 +231,13 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
[["juicily", [[0, 5]]],
|
[["juicily", [[0, 5]]],
|
||||||
["juiciness", [[0, 5]]]], matcher.match(list, 'jii', '', '').sort)
|
["juiciness", [[0, 5]]]], matcher.match(list, 'jii').sort)
|
||||||
|
matcher.cache list, 'jii', matcher.match(list, 'jii')
|
||||||
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
[["juicily", [[2, 5]]],
|
[["juicily", [[2, 5]]],
|
||||||
["juiciness", [[2, 5]]]], matcher.match(list, 'ii', '', '').sort)
|
["juiciness", [[2, 5]]]], matcher.match(list, 'ii').sort)
|
||||||
|
matcher.cache list, 'ii', matcher.match(list, 'jii')
|
||||||
|
|
||||||
assert_equal 3, matcher.caches[list.object_id].length
|
assert_equal 3, matcher.caches[list.object_id].length
|
||||||
assert_equal 2, matcher.caches[list.object_id]['ii'].length
|
assert_equal 2, matcher.caches[list.object_id]['ii'].length
|
||||||
@ -256,40 +261,40 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
def test_fuzzy_matcher_case_sensitive
|
def test_fuzzy_matcher_case_sensitive
|
||||||
# Smart-case match (Uppercase found)
|
# Smart-case match (Uppercase found)
|
||||||
assert_equal [['Fruit', [[0, 5]]]],
|
assert_equal [['Fruit', [[0, 5]]]],
|
||||||
FZF::FuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], 'Fruit', '', '').sort
|
FZF::FuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], 'Fruit').sort
|
||||||
|
|
||||||
# Smart-case match (Uppercase not-found)
|
# Smart-case match (Uppercase not-found)
|
||||||
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
|
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
|
||||||
FZF::FuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], 'fruit', '', '').sort
|
FZF::FuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], 'fruit').sort
|
||||||
|
|
||||||
# Case-sensitive match (-i)
|
# 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)
|
# 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
|
def test_extended_fuzzy_matcher_case_sensitive
|
||||||
%w['Fruit Fruit$].each do |q|
|
%w['Fruit Fruit$].each do |q|
|
||||||
# Smart-case match (Uppercase found)
|
# Smart-case match (Uppercase found)
|
||||||
assert_equal [['Fruit', [[0, 5]]]],
|
assert_equal [['Fruit', [[0, 5]]]],
|
||||||
FZF::ExtendedFuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], q, '', '').sort
|
FZF::ExtendedFuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], q).sort
|
||||||
|
|
||||||
# Smart-case match (Uppercase not-found)
|
# Smart-case match (Uppercase not-found)
|
||||||
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
|
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
|
||||||
FZF::ExtendedFuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], q.downcase, '', '').sort
|
FZF::ExtendedFuzzyMatcher.new(nil).match(%w[Fruit Grapefruit], q.downcase).sort
|
||||||
|
|
||||||
# Case-sensitive match (-i)
|
# Case-sensitive match (-i)
|
||||||
assert_equal [['Fruit', [[0, 5]]]],
|
assert_equal [['Fruit', [[0, 5]]]],
|
||||||
FZF::ExtendedFuzzyMatcher.new(0).match(%w[Fruit Grapefruit], q, '', '').sort
|
FZF::ExtendedFuzzyMatcher.new(0).match(%w[Fruit Grapefruit], q).sort
|
||||||
|
|
||||||
# Case-insensitive match (+i)
|
# Case-insensitive match (+i)
|
||||||
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
|
assert_equal [["Fruit", [[0, 5]]], ["Grapefruit", [[5, 10]]]],
|
||||||
FZF::ExtendedFuzzyMatcher.new(Regexp::IGNORECASE).
|
FZF::ExtendedFuzzyMatcher.new(Regexp::IGNORECASE).
|
||||||
match(%w[Fruit Grapefruit], q, '', '').sort
|
match(%w[Fruit Grapefruit], q).sort
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -304,7 +309,8 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
juicy
|
juicy
|
||||||
_juice]
|
_juice]
|
||||||
match = proc { |q, prefix|
|
match = proc { |q, prefix|
|
||||||
matcher.match(list, q, prefix, '').sort.map { |p| [p.first, p.last.sort] }
|
cached = matcher.cached(list, q, prefix, '')
|
||||||
|
matcher.match(list, q, cached).sort.map { |p| [p.first, p.last.sort] }
|
||||||
}
|
}
|
||||||
|
|
||||||
assert matcher.caches.empty?
|
assert matcher.caches.empty?
|
||||||
@ -366,15 +372,20 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
c.java$
|
c.java$
|
||||||
d.java
|
d.java
|
||||||
]
|
]
|
||||||
|
match = lambda do |q, p|
|
||||||
|
cached = matcher.cached(list, q, p, '')
|
||||||
|
result = matcher.match(list, q, cached)
|
||||||
|
matcher.cache list, q, result
|
||||||
|
result
|
||||||
|
end
|
||||||
2.times do
|
2.times do
|
||||||
assert_equal 5, matcher.match(list, 'java', 'java', '').length
|
assert_equal 5, match.call('java', 'java').length
|
||||||
assert_equal 3, matcher.match(list, 'java$', 'java$', '').length
|
assert_equal 3, match.call('java$', 'java$').length
|
||||||
assert_equal 1, matcher.match(list, 'java$$', 'java$$', '').length
|
assert_equal 1, match.call('java$$', 'java$$').length
|
||||||
|
assert_equal 0, match.call('!java', '!java').length
|
||||||
assert_equal 0, matcher.match(list, '!java', '!java', '').length
|
assert_equal 4, match.call('!^jav', '!^jav').length
|
||||||
assert_equal 4, matcher.match(list, '!^jav', '!^jav', '').length
|
assert_equal 4, match.call('!^java', '!^java').length
|
||||||
assert_equal 4, matcher.match(list, '!^java', '!^java', '').length
|
assert_equal 2, match.call('!^java !b !c', '!^java').length
|
||||||
assert_equal 2, matcher.match(list, '!^java !b !c', '!^java', '').length
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -400,7 +411,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.sort(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,7 +422,7 @@ 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.sort(xmatcher.match(list, '0 1', '', '')))
|
FZF.sort(xmatcher.match(list, '0 1')))
|
||||||
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
[["_01_", [[1, 3], [0, 4]], [4, 4, "_01_"]],
|
[["_01_", [[1, 3], [0, 4]], [4, 4, "_01_"]],
|
||||||
@ -420,7 +431,7 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
["0____1", [[0, 6], [1, 3]], [6, 6, "0____1"]],
|
["0____1", [[0, 6], [1, 3]], [6, 6, "0____1"]],
|
||||||
["0_____1", [[0, 7], [1, 3]], [7, 7, "0_____1"]],
|
["0_____1", [[0, 7], [1, 3]], [7, 7, "0_____1"]],
|
||||||
["0______1", [[0, 8], [1, 3]], [8, 8, "0______1"]]],
|
["0______1", [[0, 8], [1, 3]], [8, 8, "0______1"]]],
|
||||||
FZF.sort(xmatcher.match(list, '01 __', '', '')).map { |tuple|
|
FZF.sort(xmatcher.match(list, '01 __')).map { |tuple|
|
||||||
tuple << FZF.rank(tuple)
|
tuple << FZF.rank(tuple)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -433,16 +444,16 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
extended-exact-mode-not-fuzzy
|
extended-exact-mode-not-fuzzy
|
||||||
extended'-fuzzy-mode
|
extended'-fuzzy-mode
|
||||||
]
|
]
|
||||||
assert_equal 2, fuzzy.match(list, 'extended', '', '').length
|
assert_equal 2, fuzzy.match(list, 'extended').length
|
||||||
assert_equal 2, fuzzy.match(list, 'mode extended', '', '').length
|
assert_equal 2, fuzzy.match(list, 'mode extended').length
|
||||||
assert_equal 2, fuzzy.match(list, 'xtndd', '', '').length
|
assert_equal 2, fuzzy.match(list, 'xtndd').length
|
||||||
assert_equal 2, fuzzy.match(list, "'-fuzzy", '', '').length
|
assert_equal 2, fuzzy.match(list, "'-fuzzy").length
|
||||||
|
|
||||||
assert_equal 2, exact.match(list, 'extended', '', '').length
|
assert_equal 2, exact.match(list, 'extended').length
|
||||||
assert_equal 2, exact.match(list, 'mode extended', '', '').length
|
assert_equal 2, exact.match(list, 'mode extended').length
|
||||||
assert_equal 0, exact.match(list, 'xtndd', '', '').length
|
assert_equal 0, exact.match(list, 'xtndd').length
|
||||||
assert_equal 1, exact.match(list, "'-fuzzy", '', '').length
|
assert_equal 1, exact.match(list, "'-fuzzy").length
|
||||||
assert_equal 2, exact.match(list, "-fuzzy", '', '').length
|
assert_equal 2, exact.match(list, "-fuzzy").length
|
||||||
end
|
end
|
||||||
|
|
||||||
if RUBY_PLATFORM =~ /darwin/
|
if RUBY_PLATFORM =~ /darwin/
|
||||||
@ -472,16 +483,16 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
|
|
||||||
def test_nfd_fuzzy_matcher
|
def test_nfd_fuzzy_matcher
|
||||||
matcher = FZF::FuzzyMatcher.new 0
|
matcher = FZF::FuzzyMatcher.new 0
|
||||||
assert_equal [], matcher.match([NFD + NFD], '할', '', '')
|
assert_equal [], matcher.match([NFD + NFD], '할')
|
||||||
match = matcher.match([NFD + NFD], '글글', '', '')
|
match = matcher.match([NFD + NFD], '글글')
|
||||||
assert_equal [[NFD + NFD, [[3, 12]]]], match
|
assert_equal [[NFD + NFD, [[3, 12]]]], match
|
||||||
assert_equal ['한글한글', [[1, 4]]], FZF::UConv.nfc(*match.first)
|
assert_equal ['한글한글', [[1, 4]]], FZF::UConv.nfc(*match.first)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nfd_extended_fuzzy_matcher
|
def test_nfd_extended_fuzzy_matcher
|
||||||
matcher = FZF::ExtendedFuzzyMatcher.new 0
|
matcher = FZF::ExtendedFuzzyMatcher.new 0
|
||||||
assert_equal [], matcher.match([NFD], "'글글", '', '')
|
assert_equal [], matcher.match([NFD], "'글글")
|
||||||
match = matcher.match([NFD], "'한글", '', '')
|
match = matcher.match([NFD], "'한글")
|
||||||
assert_equal [[NFD, [[0, 6]]]], match
|
assert_equal [[NFD, [[0, 6]]]], match
|
||||||
assert_equal ['한글', [[0, 2]]], FZF::UConv.nfc(*match.first)
|
assert_equal ['한글', [[0, 2]]], FZF::UConv.nfc(*match.first)
|
||||||
end
|
end
|
||||||
@ -524,46 +535,46 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
]
|
]
|
||||||
|
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE
|
||||||
assert_equal list, matcher.match(list, 'f', '', '').map(&:first)
|
assert_equal list, matcher.match(list, 'f').map(&:first)
|
||||||
assert_equal [
|
assert_equal [
|
||||||
[list[0], [[2, 5]]],
|
[list[0], [[2, 5]]],
|
||||||
[list[1], [[9, 17]]]], matcher.match(list, 'is', '', '')
|
[list[1], [[9, 17]]]], matcher.match(list, 'is')
|
||||||
|
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2]
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2]
|
||||||
assert_equal [[list[1], [[8, 9]]]], matcher.match(list, 'f', '', '')
|
assert_equal [[list[1], [[8, 9]]]], matcher.match(list, 'f')
|
||||||
assert_equal [[list[0], [[8, 9]]]], matcher.match(list, 's', '', '')
|
assert_equal [[list[0], [[8, 9]]]], matcher.match(list, 's')
|
||||||
|
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3]
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3]
|
||||||
assert_equal [[list[0], [[19, 20]]]], matcher.match(list, 'r', '', '')
|
assert_equal [[list[0], [[19, 20]]]], matcher.match(list, 'r')
|
||||||
|
|
||||||
# Comma-separated
|
# Comma-separated
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3, 1]
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3, 1]
|
||||||
assert_equal [[list[0], [[19, 20]]], [list[1], [[3, 4]]]], matcher.match(list, 'r', '', '')
|
assert_equal [[list[0], [[19, 20]]], [list[1], [[3, 4]]]], matcher.match(list, 'r')
|
||||||
|
|
||||||
# Ordered
|
# Ordered
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1, 3]
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1, 3]
|
||||||
assert_equal [[list[0], [[3, 4]]], [list[1], [[3, 4]]]], matcher.match(list, 'r', '', '')
|
assert_equal [[list[0], [[3, 4]]], [list[1], [[3, 4]]]], matcher.match(list, 'r')
|
||||||
|
|
||||||
regex = FZF.build_delim_regex "\t"
|
regex = FZF.build_delim_regex "\t"
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1], regex
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1], regex
|
||||||
assert_equal [[list[0], [[3, 10]]]], matcher.match(list, 're', '', '')
|
assert_equal [[list[0], [[3, 10]]]], matcher.match(list, 're')
|
||||||
|
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex
|
||||||
assert_equal [], matcher.match(list, 'r', '', '')
|
assert_equal [], matcher.match(list, 'r')
|
||||||
assert_equal [[list[1], [[9, 17]]]], matcher.match(list, 'is', '', '')
|
assert_equal [[list[1], [[9, 17]]]], matcher.match(list, 'is')
|
||||||
|
|
||||||
# Negative indexing
|
# Negative indexing
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [-1], regex
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [-1], regex
|
||||||
assert_equal [[list[0], [[3, 6]]]], matcher.match(list, 'rt', '', '')
|
assert_equal [[list[0], [[3, 6]]]], matcher.match(list, 'rt')
|
||||||
assert_equal [[list[0], [[2, 5]]], [list[1], [[9, 17]]]], matcher.match(list, 'is', '', '')
|
assert_equal [[list[0], [[2, 5]]], [list[1], [[9, 17]]]], matcher.match(list, 'is')
|
||||||
|
|
||||||
# Regex delimiter
|
# Regex delimiter
|
||||||
regex = FZF.build_delim_regex "[ \t]+"
|
regex = FZF.build_delim_regex "[ \t]+"
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1], regex
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1], regex
|
||||||
assert_equal [list[1]], matcher.match(list, 'f', '', '').map(&:first)
|
assert_equal [list[1]], matcher.match(list, 'f').map(&:first)
|
||||||
|
|
||||||
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex
|
matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex
|
||||||
assert_equal [[list[0], [[1, 2]]], [list[1], [[8, 9]]]], matcher.match(list, 'f', '', '')
|
assert_equal [[list[0], [[1, 2]]], [list[1], [[8, 9]]]], matcher.match(list, 'f')
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream_for str
|
def stream_for str
|
||||||
@ -659,7 +670,7 @@ class TestFZF < MiniTest::Unit::TestCase
|
|||||||
assert_equal [
|
assert_equal [
|
||||||
['1 2 3 4', [[0, 13], [16, 22]]],
|
['1 2 3 4', [[0, 13], [16, 22]]],
|
||||||
['1 3 4 2', [[0, 24], [12, 17]]],
|
['1 3 4 2', [[0, 24], [12, 17]]],
|
||||||
], FZF.sort(FZF::ExtendedFuzzyMatcher.new(nil).match(list, '12 34', '', ''))
|
], FZF.sort(FZF::ExtendedFuzzyMatcher.new(nil).match(list, '12 34'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user