diff --git a/README.md b/README.md index baa73f2c84e57d44323771ca93dd77fc5dc1e2e6..6e2c22469f0d2359e6e490ef561a3f35de606f83 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ usage: fzf [options] -i Case-insensitive match (default: smart-case match) +i Case-sensitive match +c, --no-color Disable colors - --no-mouse Disable mouse Environment variables FZF_DEFAULT_COMMAND Default command to use when input is tty @@ -102,9 +101,6 @@ The following readline key bindings should also work as expected. If you enable multi-select mode with `-m` option, you can select multiple items with TAB or Shift-TAB key. -You can also use mouse. Click on an item to select it or shift-click to select -multiple items. Use mouse wheel to move the cursor up and down. - ### Extended-search mode With `-x` or `--extended` option, fzf will start in "extended-search mode". diff --git a/fzf b/fzf index 5426e53d335037d33dd95b4be7a9c21f621f4bc5..97b664c5a24160865c1c48a664ceb99b4787f526 100755 --- a/fzf +++ b/fzf @@ -7,7 +7,7 @@ # / __/ / /_/ __/ # /_/ /___/_/ Fuzzy finder for your shell # -# Version: 0.7.3 (March 5, 2014) +# Version: 0.8.0 (March 6, 2014) # # Author: Junegunn Choi # URL: https://github.com/junegunn/fzf @@ -50,7 +50,7 @@ end class FZF C = Curses - attr_reader :rxflag, :sort, :color, :mouse, :multi, :query, :filter, :extended + attr_reader :rxflag, :sort, :color, :multi, :query, :filter, :extended class AtomicVar def initialize value @@ -79,9 +79,7 @@ class FZF @color = true @multi = false @extended = nil - @mouse = true @filter = nil - @pending = nil argv = if opts = ENV['FZF_DEFAULT_OPTS'] @@ -102,7 +100,6 @@ class FZF when '+i' then @rxflag = 0 when '-c', '--color' then @color = true when '+c', '--no-color' then @color = false - when '--no-mouse' then @mouse = false when '+s', '--no-sort' then @sort = nil when '-q', '--query' usage 1, 'query string required' unless query = argv.shift @@ -129,11 +126,11 @@ class FZF @source = source.clone @mtx = Mutex.new - @rmtx = Mutex.new @cv = ConditionVariable.new @events = {} @new = [] @queue = Queue.new + @pending = nil unless @filter @query ||= AtomicVar.new('') @@ -207,7 +204,6 @@ class FZF -i Case-insensitive match (default: smart-case match) +i Case-sensitive match +c, --no-color Disable colors - --no-mouse Disable mouse Environment variables FZF_DEFAULT_COMMAND Default command to use when input is tty @@ -510,11 +506,6 @@ class FZF def init_screen C.init_screen - if @mouse - C.mouseinterval 0 - C.mousemask C::ALL_MOUSE_EVENTS - end - C.stdscr.keypad(true) C.start_color dbg = if C.respond_to?(:use_default_colors) @@ -524,7 +515,6 @@ class FZF C::COLOR_BLACK end C.raw - C.nonl C.noecho if @color @@ -582,7 +572,6 @@ class FZF exit 1 end else - $stdin.reopen IO.open(IO.sysopen('/dev/tty'), 'r') if curses @source end @@ -722,10 +711,8 @@ class FZF Thread.new do begin while blk = @queue.shift - @rmtx.synchronize do - blk.call - refresh - end + blk.call + refresh end rescue Exception => e @main.raise e @@ -757,54 +744,67 @@ class FZF end end - def test_mouse st, *states - states.any? { |s| s & st > 0 } - end - - def to_printable ch - if ch.is_a?(Fixnum) - # Ruby 1.8 - if (ch.chr rescue '') =~ /[[:print:]]/ - ch = ch.chr - elsif (nch = num_unicode_bytes(ch)) > 1 - chs = [ch] - (nch - 1).times do |i| - chs << getch_nb - end - # UTF-8 TODO Ruby 1.8 - ch = chs.pack('C*').force_encoding('UTF-8') - end - end + def get_input actions + @tty ||= IO.open(IO.sysopen('/dev/tty'), 'r') - ch.is_a?(String) && ch =~ /[[:print:]]/ && ch - end - - def getch_nb - @rmtx.synchronize { C.getch } - end - - def getch if pending = @pending @pending = nil return pending end - C.stdscr.timeout = -1 - c = C.getch - C.stdscr.timeout = 0 - if ch = to_printable(c) - chs = [ch] - while AFTER_1_9 && c = getch_nb - if ch = to_printable(c) - chs << ch + str = '' + while true + ord = + if str.empty? + @tty.getc.ord else - @pending = c - break + begin + ord = @tty.read_nonblock(1).ord + if (nb = num_unicode_bytes(ord)) > 1 + ords = [ord] + (nb - 1).times do |_| + ords << @tty.read_nonblock(1).ord + end + # UTF-8 TODO Ruby 1.8 + ords.pack('C*').force_encoding('UTF-8') + else + ord + end + rescue Exception + return str + end end + + ord = + case ord = (@tty.read_nonblock(1).ord rescue :esc) + when 91 + case (@tty.read_nonblock(1).ord rescue nil) + when 68 then ctrl(:b) + when 67 then ctrl(:f) + when 66 then ctrl(:j) + when 65 then ctrl(:k) + when 90 then :stab + else next + end + when 'b', 98 then :alt_b + when 'f', 102 then :alt_f + when :esc then :esc + else next + end if ord == 27 + + if actions.has_key?(ord) + if str.empty? + return ord + else + @pending = ord + return str + end + else + unless ord.is_a? String + ord = [ord].pack('U*') + end + str << ord if ord =~ /[[:print:]]/ end - chs - else - c end end @@ -841,13 +841,7 @@ class FZF else @selects[sel] = 1 end - vselect { |v| - v + case o - when :select then 0 - when C::KEY_BTAB then 1 - else -1 - end - } + vselect { |v| v + (o == :stab ? 1 : -1) } end }, ctrl(:b) => proc { cursor = [0, cursor - 1].max; nil }, @@ -859,76 +853,26 @@ class FZF nil }, } - actions[C::KEY_UP] = actions[ctrl(:p)] = actions[ctrl(:k)] - actions[C::KEY_DOWN] = actions[ctrl(:n)] = actions[ctrl(:j)] - actions[C::KEY_LEFT] = actions[ctrl(:b)] - actions[C::KEY_RIGHT] = actions[ctrl(:f)] - actions[C::KEY_BTAB] = actions[:select] = actions[ctrl(:i)] - actions[C::KEY_BACKSPACE] = actions[127] = actions[ctrl(:h)] + actions[ctrl(:p)] = actions[ctrl(:k)] + actions[ctrl(:n)] = actions[ctrl(:j)] + actions[:stab] = actions[ctrl(:i)] + actions[127] = actions[ctrl(:h)] actions[ctrl(:q)] = actions[ctrl(:g)] = actions[ctrl(:c)] = actions[:esc] emit(:key) { [@query.get, cursor] } unless @query.empty? - pmv = nil while true @cursor_x.set cursor render { print_input } - case ch = getch - when C::KEY_MOUSE - if m = C.getmouse - st = m.bstate - if test_mouse(st, C::BUTTON1_PRESSED, C::BUTTON1_RELEASED) - if m.y == cursor_y - # TODO Wide-characters - cursor = [0, [input.length, m.x - 2].min].max - elsif m.x > 1 && m.y <= max_items - vselect { |v| - tv = max_items - m.y - 1 - if test_mouse(st, C::BUTTON1_RELEASED) - if test_mouse(st, C::BUTTON_SHIFT) - ch = :select - elsif pmv == tv - ch = ctrl(:m) - end - pmv = tv - end - tv - } - end - elsif test_mouse(st, 0x8000000, C::BUTTON2_PRESSED) - ch = C::KEY_DOWN - elsif test_mouse(st, C::BUTTON4_PRESSED) - ch = C::KEY_UP - end - end - when 27 - C.stdscr.timeout = 0 - ch = # Typeahead arrow keys - case ch2 = getch_nb - when '[', 91 - case ch3 = getch_nb - when 'D', 68 then ctrl(:b) - when 'C', 67 then ctrl(:f) - when 'B', 66 then ctrl(:j) - when 'A', 65 then ctrl(:k) - else ch3 - end - when 'b', 98 then :alt_b - when 'f', 102 then :alt_f - when nil then :esc - else ch2 - end - end - - upd = actions.fetch(ch, proc { |ch| - if ch.is_a? Array - input.insert cursor, ch.join - cursor += ch.length - end - }).call(ch) + if key = get_input(actions) + upd = actions.fetch(key, proc { |str| + input.insert cursor, str + cursor += str.length + }).call(key) - # Dispatch key event - emit(:key) { [@query.set(input.dup), cursor] } if upd + # Dispatch key event + emit(:key) { [@query.set(input.dup), cursor] } if upd + end end ensure C.close_screen diff --git a/test/test_fzf.rb b/test/test_fzf.rb index d100665971daaa3f10e7da0c3539e0c1cb45c229..65b105ae921163a4108e30073c979d676a83754a 100644 --- a/test/test_fzf.rb +++ b/test/test_fzf.rb @@ -20,7 +20,6 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal false, fzf.multi assert_equal true, fzf.color assert_equal nil, fzf.rxflag - assert_equal true, fzf.mouse end def test_environment_variables @@ -29,7 +28,7 @@ class TestFZF < MiniTest::Unit::TestCase fzf = FZF.new [] assert_equal 20000, fzf.sort - ENV['FZF_DEFAULT_OPTS'] = '-x -m -s 10000 -q " hello world " +c --no-mouse -f "goodbye world"' + ENV['FZF_DEFAULT_OPTS'] = '-x -m -s 10000 -q " hello world " +c -f "goodbye world"' fzf = FZF.new [] assert_equal 10000, fzf.sort assert_equal ' hello world ', @@ -39,17 +38,15 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal :fuzzy, fzf.extended assert_equal true, fzf.multi assert_equal false, fzf.color - assert_equal false, fzf.mouse end def test_option_parser # Long opts fzf = FZF.new %w[--sort=2000 --no-color --multi +i --query hello - --filter=howdy --extended-exact --no-mouse] + --filter=howdy --extended-exact] assert_equal 2000, fzf.sort assert_equal true, fzf.multi assert_equal false, fzf.color - assert_equal false, fzf.mouse assert_equal 0, fzf.rxflag assert_equal 'hello', fzf.query.get assert_equal 'howdy', fzf.filter @@ -61,7 +58,6 @@ class TestFZF < MiniTest::Unit::TestCase assert_equal nil, fzf.sort assert_equal false, fzf.multi assert_equal true, fzf.color - assert_equal true, fzf.mouse assert_equal 1, fzf.rxflag assert_equal 'b', fzf.filter assert_equal 'hello', fzf.query.get