Completely remove mouse support

Since the version 0.7.0, fzf internally used Curses.getch() call to take user
input, which allowed it to support mouse input as well. However it has turned
out that Curses.getch() has introduced glitches that cannot be easily handled
(e.g. Try resize the terminal). So I finally decided that it's not worth the
trouble and drop the mouse support.
This commit is contained in:
Junegunn Choi 2014-03-06 12:21:09 +09:00
parent 65ae6cabb5
commit 2fb8ae010f
3 changed files with 72 additions and 136 deletions

View File

@ -58,7 +58,6 @@ usage: fzf [options]
-i Case-insensitive match (default: smart-case match) -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
--no-mouse Disable mouse
Environment variables Environment variables
FZF_DEFAULT_COMMAND Default command to use when input is tty 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 If you enable multi-select mode with `-m` option, you can select multiple items
with TAB or Shift-TAB key. 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 ### Extended-search mode
With `-x` or `--extended` option, fzf will start in "extended-search mode". With `-x` or `--extended` option, fzf will start in "extended-search mode".

196
fzf
View File

@ -7,7 +7,7 @@
# / __/ / /_/ __/ # / __/ / /_/ __/
# /_/ /___/_/ Fuzzy finder for your shell # /_/ /___/_/ Fuzzy finder for your shell
# #
# Version: 0.7.3 (March 5, 2014) # Version: 0.8.0 (March 6, 2014)
# #
# Author: Junegunn Choi # Author: Junegunn Choi
# URL: https://github.com/junegunn/fzf # URL: https://github.com/junegunn/fzf
@ -50,7 +50,7 @@ end
class FZF class FZF
C = Curses C = Curses
attr_reader :rxflag, :sort, :color, :mouse, :multi, :query, :filter, :extended attr_reader :rxflag, :sort, :color, :multi, :query, :filter, :extended
class AtomicVar class AtomicVar
def initialize value def initialize value
@ -79,9 +79,7 @@ class FZF
@color = true @color = true
@multi = false @multi = false
@extended = nil @extended = nil
@mouse = true
@filter = nil @filter = nil
@pending = nil
argv = argv =
if opts = ENV['FZF_DEFAULT_OPTS'] if opts = ENV['FZF_DEFAULT_OPTS']
@ -102,7 +100,6 @@ class FZF
when '+i' then @rxflag = 0 when '+i' then @rxflag = 0
when '-c', '--color' then @color = true when '-c', '--color' then @color = true
when '+c', '--no-color' then @color = false when '+c', '--no-color' then @color = false
when '--no-mouse' then @mouse = false
when '+s', '--no-sort' then @sort = nil when '+s', '--no-sort' then @sort = nil
when '-q', '--query' when '-q', '--query'
usage 1, 'query string required' unless query = argv.shift usage 1, 'query string required' unless query = argv.shift
@ -129,11 +126,11 @@ class FZF
@source = source.clone @source = source.clone
@mtx = Mutex.new @mtx = Mutex.new
@rmtx = Mutex.new
@cv = ConditionVariable.new @cv = ConditionVariable.new
@events = {} @events = {}
@new = [] @new = []
@queue = Queue.new @queue = Queue.new
@pending = nil
unless @filter unless @filter
@query ||= AtomicVar.new('') @query ||= AtomicVar.new('')
@ -207,7 +204,6 @@ class FZF
-i Case-insensitive match (default: smart-case match) -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
--no-mouse Disable mouse
Environment variables Environment variables
FZF_DEFAULT_COMMAND Default command to use when input is tty FZF_DEFAULT_COMMAND Default command to use when input is tty
@ -510,11 +506,6 @@ class FZF
def init_screen def init_screen
C.init_screen C.init_screen
if @mouse
C.mouseinterval 0
C.mousemask C::ALL_MOUSE_EVENTS
end
C.stdscr.keypad(true)
C.start_color C.start_color
dbg = dbg =
if C.respond_to?(:use_default_colors) if C.respond_to?(:use_default_colors)
@ -524,7 +515,6 @@ class FZF
C::COLOR_BLACK C::COLOR_BLACK
end end
C.raw C.raw
C.nonl
C.noecho C.noecho
if @color if @color
@ -582,7 +572,6 @@ class FZF
exit 1 exit 1
end end
else else
$stdin.reopen IO.open(IO.sysopen('/dev/tty'), 'r') if curses
@source @source
end end
@ -722,10 +711,8 @@ class FZF
Thread.new do Thread.new do
begin begin
while blk = @queue.shift while blk = @queue.shift
@rmtx.synchronize do blk.call
blk.call refresh
refresh
end
end end
rescue Exception => e rescue Exception => e
@main.raise e @main.raise e
@ -757,54 +744,67 @@ class FZF
end end
end end
def test_mouse st, *states def get_input actions
states.any? { |s| s & st > 0 } @tty ||= IO.open(IO.sysopen('/dev/tty'), 'r')
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
ch.is_a?(String) && ch =~ /[[:print:]]/ && ch
end
def getch_nb
@rmtx.synchronize { C.getch }
end
def getch
if pending = @pending if pending = @pending
@pending = nil @pending = nil
return pending return pending
end end
C.stdscr.timeout = -1 str = ''
c = C.getch while true
C.stdscr.timeout = 0 ord =
if ch = to_printable(c) if str.empty?
chs = [ch] @tty.getc.ord
while AFTER_1_9 && c = getch_nb
if ch = to_printable(c)
chs << ch
else else
@pending = c begin
break 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 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 end
chs
else
c
end end
end end
@ -841,13 +841,7 @@ class FZF
else else
@selects[sel] = 1 @selects[sel] = 1
end end
vselect { |v| vselect { |v| v + (o == :stab ? 1 : -1) }
v + case o
when :select then 0
when C::KEY_BTAB then 1
else -1
end
}
end end
}, },
ctrl(:b) => proc { cursor = [0, cursor - 1].max; nil }, ctrl(:b) => proc { cursor = [0, cursor - 1].max; nil },
@ -859,76 +853,26 @@ class FZF
nil nil
}, },
} }
actions[C::KEY_UP] = actions[ctrl(:p)] = actions[ctrl(:k)] actions[ctrl(:p)] = actions[ctrl(:k)]
actions[C::KEY_DOWN] = actions[ctrl(:n)] = actions[ctrl(:j)] actions[ctrl(:n)] = actions[ctrl(:j)]
actions[C::KEY_LEFT] = actions[ctrl(:b)] actions[:stab] = actions[ctrl(:i)]
actions[C::KEY_RIGHT] = actions[ctrl(:f)] actions[127] = actions[ctrl(:h)]
actions[C::KEY_BTAB] = actions[:select] = actions[ctrl(:i)]
actions[C::KEY_BACKSPACE] = actions[127] = actions[ctrl(:h)]
actions[ctrl(:q)] = actions[ctrl(:g)] = actions[ctrl(:c)] = actions[:esc] actions[ctrl(:q)] = actions[ctrl(:g)] = actions[ctrl(:c)] = actions[:esc]
emit(:key) { [@query.get, cursor] } unless @query.empty? emit(:key) { [@query.get, cursor] } unless @query.empty?
pmv = nil
while true while true
@cursor_x.set cursor @cursor_x.set cursor
render { print_input } render { print_input }
case ch = getch if key = get_input(actions)
when C::KEY_MOUSE upd = actions.fetch(key, proc { |str|
if m = C.getmouse input.insert cursor, str
st = m.bstate cursor += str.length
if test_mouse(st, C::BUTTON1_PRESSED, C::BUTTON1_RELEASED) }).call(key)
if m.y == cursor_y
# TODO Wide-characters # Dispatch key event
cursor = [0, [input.length, m.x - 2].min].max emit(:key) { [@query.set(input.dup), cursor] } if upd
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 end
upd = actions.fetch(ch, proc { |ch|
if ch.is_a? Array
input.insert cursor, ch.join
cursor += ch.length
end
}).call(ch)
# Dispatch key event
emit(:key) { [@query.set(input.dup), cursor] } if upd
end end
ensure ensure
C.close_screen C.close_screen

View File

@ -20,7 +20,6 @@ class TestFZF < MiniTest::Unit::TestCase
assert_equal false, fzf.multi assert_equal false, fzf.multi
assert_equal true, fzf.color assert_equal true, fzf.color
assert_equal nil, fzf.rxflag assert_equal nil, fzf.rxflag
assert_equal true, fzf.mouse
end end
def test_environment_variables def test_environment_variables
@ -29,7 +28,7 @@ class TestFZF < MiniTest::Unit::TestCase
fzf = FZF.new [] fzf = FZF.new []
assert_equal 20000, fzf.sort 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 [] fzf = FZF.new []
assert_equal 10000, fzf.sort assert_equal 10000, fzf.sort
assert_equal ' hello world ', assert_equal ' hello world ',
@ -39,17 +38,15 @@ class TestFZF < MiniTest::Unit::TestCase
assert_equal :fuzzy, fzf.extended assert_equal :fuzzy, fzf.extended
assert_equal true, fzf.multi assert_equal true, fzf.multi
assert_equal false, fzf.color assert_equal false, fzf.color
assert_equal false, fzf.mouse
end end
def test_option_parser def test_option_parser
# Long opts # Long opts
fzf = FZF.new %w[--sort=2000 --no-color --multi +i --query hello 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 2000, fzf.sort
assert_equal true, fzf.multi assert_equal true, fzf.multi
assert_equal false, fzf.color assert_equal false, fzf.color
assert_equal false, fzf.mouse
assert_equal 0, fzf.rxflag assert_equal 0, fzf.rxflag
assert_equal 'hello', fzf.query.get assert_equal 'hello', fzf.query.get
assert_equal 'howdy', fzf.filter assert_equal 'howdy', fzf.filter
@ -61,7 +58,6 @@ class TestFZF < MiniTest::Unit::TestCase
assert_equal nil, fzf.sort assert_equal nil, fzf.sort
assert_equal false, fzf.multi assert_equal false, fzf.multi
assert_equal true, fzf.color assert_equal true, fzf.color
assert_equal true, fzf.mouse
assert_equal 1, fzf.rxflag assert_equal 1, fzf.rxflag
assert_equal 'b', fzf.filter assert_equal 'b', fzf.filter
assert_equal 'hello', fzf.query.get assert_equal 'hello', fzf.query.get