From ab9fbf196721c171d8ebe5545a074e500c6fb908 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Wed, 2 Apr 2014 01:49:07 +0900 Subject: [PATCH] Allow --nth option to take multiple indexes (comma-separated) --- README.md | 10 +++++++--- fzf | 32 ++++++++++++++++++++++---------- test/test_fzf.rb | 38 +++++++++++++++++++++++--------------- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 3730d83..db02bec 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ usage: fzf [options] -e, --extended-exact Extended-search mode (exact match) -q, --query=STR Initial query -f, --filter=STR Filter mode. Do not start interactive finder. - -n, --nth=[-]N Match only in the N-th token of the item + -n, --nth=[-]N[,..] Comma-separated list of field indexes for limiting + search scope (positive or negative integers) -d, --delimiter=STR Field delimiter regex for --nth (default: AWK-style) -s, --sort=MAX Maximum number of matched items to sort (default: 1000) +s, --no-sort Do not sort the result. Keep the sequence unchanged. @@ -186,8 +187,11 @@ fco() { ftags() { local line [ -e tags ] && - line=$(grep -v "^!" tags | cut -f1-3 | cut -c1-80 | fzf --nth=1) && - $EDITOR $(cut -f2 <<< "$line") + line=$( + awk 'BEGIN { FS="\t" } !/^!/ {print toupper($4)"\t"$1"\t"$2"\t"$3}' tags | + cut -c1-80 | fzf --nth=1,2 + ) && $EDITOR $(cut -f3 <<< "$line") -c "set nocst" \ + -c "silent tag $(cut -f2 <<< "$line")" } # fq1 [QUERY] diff --git a/fzf b/fzf index a623e00..b1313c5 100755 --- a/fzf +++ b/fzf @@ -7,7 +7,7 @@ # / __/ / /_/ __/ # /_/ /___/_/ Fuzzy finder for your shell # -# Version: 0.8.2 (March 30, 2014) +# Version: 0.8.3 (April 2, 2014) # # Author: Junegunn Choi # URL: https://github.com/junegunn/fzf @@ -126,9 +126,9 @@ class FZF when '-n', '--nth' usage 1, 'field number required' unless nth = argv.shift usage 1, 'invalid field number' if nth.to_i == 0 - @nth = nth.to_i - when /^-n(-?[1-9][0-9]*)$/, /^--nth=(-?[1-9][0-9]*)$/ - @nth = $1.to_i + @nth = parse_nth nth + when /^-n([0-9,-]+)$/, /^--nth=([0-9,-]+)$/ + @nth = parse_nth $1 when '-d', '--delimiter' usage 1, 'delimiter required' unless delim = argv.shift @delim = FZF.build_delim_regex delim @@ -169,6 +169,14 @@ class FZF end end + def parse_nth nth + nth.split(',').map { |n| + ni = n.to_i + usage 1, "invalid field number: #{n}" if ni == 0 + ni + } + end + def FZF.build_delim_regex delim Regexp.compile(delim) rescue (delim = Regexp.escape(delim)) Regexp.compile "(?:.*?#{delim})|(?:.+?$)" @@ -227,7 +235,8 @@ class FZF -e, --extended-exact Extended-search mode (exact match) -q, --query=STR Initial query -f, --filter=STR Filter mode. Do not start interactive finder. - -n, --nth=[-]N Match only in the N-th token of the item + -n, --nth=[-]N[,..] Comma-separated list of field indexes for limiting + search scope (positive or negative integers) -d, --delimiter=STR Field delimiter regex for --nth (default: AWK-style) -s, --sort=MAX Maximum number of matched items to sort (default: 1000) +s, --no-sort Do not sort the result. Keep the sequence unchanged. @@ -1059,7 +1068,7 @@ class FZF end def initialize nth, delim - @nth = nth && (nth > 0 ? nth - 1 : nth) + @nth = nth && nth.map { |n| n > 0 ? n - 1 : n } @delim = delim @tokens_cache = {} end @@ -1080,11 +1089,14 @@ class FZF if @nth prefix_length, tokens = tokenize str - if (token = tokens[@nth]) && (md = token.match(pat) rescue nil) - prefix_length += (tokens[0...@nth] || []).join.length - offset = md.offset(0).map { |o| o + prefix_length } - MatchData.new offset + @nth.each do |n| + if (token = tokens[n]) && (md = token.match(pat) rescue nil) + prefix_length += (tokens[0...n] || []).join.length + offset = md.offset(0).map { |o| o + prefix_length } + return MatchData.new offset + end end + nil else str.match(pat) rescue nil end diff --git a/test/test_fzf.rb b/test/test_fzf.rb index c9ac904..e180878 100644 --- a/test/test_fzf.rb +++ b/test/test_fzf.rb @@ -30,7 +30,7 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal nil, fzf.nth ENV['FZF_DEFAULT_OPTS'] = - '-x -m -s 10000 -q " hello world " +c +2 --no-mouse -f "goodbye world" --black --nth=3' + '-x -m -s 10000 -q " hello world " +c +2 --no-mouse -f "goodbye world" --black --nth=3,-1,2' fzf = FZF.new [] assert_equal 10000, fzf.sort assert_equal ' hello world ', @@ -43,7 +43,7 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal false, fzf.ansi256 assert_equal true, fzf.black assert_equal false, fzf.mouse - assert_equal 3, fzf.nth + assert_equal [3, -1, 2], fzf.nth end def test_option_parser @@ -60,10 +60,10 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal 'hello', fzf.query.get assert_equal 'howdy', fzf.filter assert_equal :exact, fzf.extended - assert_equal 1, fzf.nth + assert_equal [1], fzf.nth fzf = FZF.new %w[--sort=2000 --no-color --multi +i --query hello - --filter a --filter b --no-256 --black --nth 2 + --filter a --filter b --no-256 --black --nth -2 --no-sort -i --color --no-multi --256] assert_equal nil, fzf.sort assert_equal false, fzf.multi @@ -75,7 +75,7 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal 'b', fzf.filter assert_equal 'hello', fzf.query.get assert_equal nil, fzf.extended - assert_equal 2, fzf.nth + assert_equal [-2], fzf.nth # Short opts fzf = FZF.new %w[-s 2000 +c -m +i -qhello -x -fhowdy +2 -n3] @@ -87,10 +87,10 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal 'hello', fzf.query.get assert_equal 'howdy', fzf.filter assert_equal :fuzzy, fzf.extended - assert_equal 3, fzf.nth + assert_equal [3], fzf.nth # Left-to-right - fzf = FZF.new %w[-s 2000 +c -m +i -qhello -x -fgoodbye +2 -n3 -n4 + fzf = FZF.new %w[-s 2000 +c -m +i -qhello -x -fgoodbye +2 -n3 -n4,5 -s 3000 -c +m -i -q world +x -fworld -2 --black --no-black] assert_equal 3000, fzf.sort assert_equal false, fzf.multi @@ -101,7 +101,7 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal 'world', fzf.query.get assert_equal 'world', fzf.filter assert_equal nil, fzf.extended - assert_equal 4, fzf.nth + assert_equal [4, 5], fzf.nth fzf = FZF.new %w[--query hello +s -s 2000 --query=world] assert_equal 2000, fzf.sort @@ -502,32 +502,40 @@ class TestFZF < MiniTest::Unit::TestCase [list[0], [[2, 5]]], [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[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', '', '') + # Comma-separated + matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [3, 1] + assert_equal [[list[0], [[19, 20]]], [list[1], [[3, 4]]]], matcher.match(list, 'r', '', '') + + # Ordered + matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [1, 3] + assert_equal [[list[0], [[3, 4]]], [list[1], [[3, 4]]]], matcher.match(list, 'r', '', '') + 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', '', '') - matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, 2, regex + matcher = FZF::FuzzyMatcher.new Regexp::IGNORECASE, [2], regex assert_equal [], matcher.match(list, 'r', '', '') assert_equal [[list[1], [[9, 17]]]], matcher.match(list, 'is', '', '') # 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], [[2, 5]]], [list[1], [[9, 17]]]], matcher.match(list, 'is', '', '') # Regex delimiter 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) - 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', '', '') end end