Implement multi-select mode (#3)

This commit is contained in:
Junegunn Choi 2013-11-10 03:56:18 +09:00
parent 11a1010e9e
commit ddf6e5ef1e
3 changed files with 44 additions and 12 deletions

View File

@ -71,6 +71,7 @@ Usage
``` ```
usage: fzf [options] usage: fzf [options]
-m, --multi Enable multi-select
-s, --sort=MAX Maximum number of matched items to sort. Default: 500 -s, --sort=MAX Maximum number of matched items to sort. Default: 500
+s, --no-sort Keep the sequence unchanged. +s, --no-sort Keep the sequence unchanged.
+i Case-sensitive match +i Case-sensitive match
@ -110,6 +111,9 @@ The following readline key bindings should also work as expected.
- CTRL-B / CTRL-F - CTRL-B / CTRL-F
- CTRL-W / CTRL-U - CTRL-W / CTRL-U
If you enable multi-select mode with `-m` option, you can select multiple items
with TAB key.
Usage as Vim plugin Usage as Vim plugin
------------------- -------------------

50
fzf
View File

@ -10,7 +10,7 @@
# URL: https://github.com/junegunn/fzf # URL: https://github.com/junegunn/fzf
# Author: Junegunn Choi # Author: Junegunn Choi
# License: MIT # License: MIT
# Last update: November 4, 2013 # Last update: November 10, 2013
# #
# Copyright (c) 2013 Junegunn Choi # Copyright (c) 2013 Junegunn Choi
# #
@ -38,6 +38,7 @@
def usage x def usage x
puts %[usage: fzf [options] puts %[usage: fzf [options]
-m, --multi Enable multi-select
-s, --sort=MAX Maximum number of matched items to sort. Default: 500. -s, --sort=MAX Maximum number of matched items to sort. Default: 500.
+s, --no-sort Do not sort the result. Keep the sequence unchanged. +s, --no-sort Do not sort the result. Keep the sequence unchanged.
+i Case-sensitive match +i Case-sensitive match
@ -50,8 +51,9 @@ $stdout.reopen($stderr)
usage 0 unless (%w[--help -h] & ARGV).empty? usage 0 unless (%w[--help -h] & ARGV).empty?
@rxflag = ARGV.delete('+i') ? 0 : Regexp::IGNORECASE @rxflag = ARGV.delete('+i') ? 0 : Regexp::IGNORECASE
@sort = (ARGV.delete('+s') || ARGV.delete('--no-sort')) ? nil : 500 @sort = %w[+s --no-sort].map { |e| ARGV.delete e }.compact.empty? ? 500 : nil
@color = (ARGV.delete('+c') || ARGV.delete('--no-color')).nil? @color = %w[+c --no-color].map { |e| ARGV.delete e }.compact.empty?
@multi = !%w[-m --multi].map { |e| ARGV.delete e }.compact.empty?
rest = ARGV.join ' ' rest = ARGV.join ' '
if sort = rest.match(/(-s|--sort=?) ?([0-9]+)/) if sort = rest.match(/(-s|--sort=?) ?([0-9]+)/)
usage 1 unless @sort usage 1 unless @sort
@ -74,6 +76,7 @@ require 'curses'
@cursor_x = 0 @cursor_x = 0
@vcursor = 0 @vcursor = 0
@events = {} @events = {}
@selects = {} # ordered >= 1.9
case RUBY_PLATFORM case RUBY_PLATFORM
when /darwin/ when /darwin/
@ -180,7 +183,7 @@ def print_input
end end
end end
def print_info msg = nil def print_info selected, msg = nil
@fan ||= '-\|/-\|/'.split(//) @fan ||= '-\|/-\|/'.split(//)
C.setpos cursor_y - 1, 0 C.setpos cursor_y - 1, 0
C.clrtoeol C.clrtoeol
@ -194,6 +197,7 @@ def print_info msg = nil
end end
C.attron color(:info, false) do C.attron color(:info, false) do
C.addstr "#{prefix}#{@matches.length}/#{@count}" C.addstr "#{prefix}#{@matches.length}/#{@count}"
C.addstr " (#{selected})" if selected > 0
C.addstr msg if msg C.addstr msg if msg
end end
end end
@ -329,6 +333,7 @@ searcher = Thread.new {
events = {} events = {}
fcache = {} fcache = {}
matches = [] matches = []
selects = {}
mcount = 0 # match count mcount = 0 # match count
plcount = 0 # prev list count plcount = 0 # prev list count
q = '' q = ''
@ -355,15 +360,18 @@ searcher = Thread.new {
@new = [] @new = []
fcache = {} fcache = {}
end end
if events[:select]
selects = @selects.dup
end
end#mtx end#mtx
new_search = events[:key] || events[:new] new_search = events[:key] || events.delete(:new)
user_input = events[:key] || events[:vcursor] user_input = events[:key] || events[:vcursor] || events.delete(:select)
progress = 0 progress = 0
started_at = Time.now started_at = Time.now
if new_search && !@lists.empty? if new_search && !@lists.empty?
events.delete :new
q = events.delete(:key) || q q = events.delete(:key) || q
unless q.empty? unless q.empty?
@ -392,7 +400,7 @@ searcher = Thread.new {
found.concat(cache[q] ||= q.empty? ? list : begin found.concat(cache[q] ||= q.empty? ? list : begin
if progress < 100 && Time.now - started_at > 0.5 if progress < 100 && Time.now - started_at > 0.5
@smtx.synchronize do @smtx.synchronize do
print_info " (#{progress}%)" print_info selects.length, " (#{progress}%)"
refresh refresh
end end
end end
@ -482,7 +490,9 @@ searcher = Thread.new {
C.setpos row, 0 C.setpos row, 0
C.clrtoeol C.clrtoeol
cprint chosen ? '>' : ' ', color(:red, true) cprint chosen ? '>' : ' ', color(:red, true)
cprint ' ', chosen ? color(:chosen) : 0 selected = selects.include?([*item][0])
cprint selected ? '>' : ' ',
chosen ? color(:chosen) : (selected ? color(:red, true) : 0)
C.attron color(:chosen, true) if chosen C.attron color(:chosen, true) if chosen
@ -497,7 +507,7 @@ searcher = Thread.new {
C.attroff color(:chosen, true) if chosen C.attroff color(:chosen, true) if chosen
end end
print_info if !@lists.empty? || events[:loaded] print_info selects.length if !@lists.empty? || events[:loaded]
refresh refresh
end end
end#while end#while
@ -532,6 +542,18 @@ begin
cursor = ridx cursor = ridx
}, },
127 => proc { input[cursor -= 1] = '' if cursor > 0 }, 127 => proc { input[cursor -= 1] = '' if cursor > 0 },
9 => proc {
emit(:select) {
if sel = [*@matches.fetch(@vcursor, [])][0]
if @selects.has_key? sel
@selects.delete sel
else
@selects[sel] = 1
end
@vcursor = [0, @vcursor - 1].max
end
} if @multi
},
:left => proc { cursor = [0, cursor - 1].max }, :left => proc { cursor = [0, cursor - 1].max },
:right => proc { cursor = [input.length, cursor + 1].min }, :right => proc { cursor = [input.length, cursor + 1].min },
} }
@ -576,6 +598,12 @@ begin
end end
ensure ensure
C.close_screen C.close_screen
stdout.puts got if got if got
@selects.delete got
@selects.each do |sel, _|
stdout.puts sel
end
stdout.puts got
end
end end

View File

@ -1,7 +1,7 @@
# coding: utf-8 # coding: utf-8
Gem::Specification.new do |spec| Gem::Specification.new do |spec|
spec.name = 'fzf' spec.name = 'fzf'
spec.version = '0.2.2' spec.version = '0.3.0'
spec.authors = ['Junegunn Choi'] spec.authors = ['Junegunn Choi']
spec.email = ['junegunn.c@gmail.com'] spec.email = ['junegunn.c@gmail.com']
spec.description = %q{Fuzzy finder for your shell} spec.description = %q{Fuzzy finder for your shell}