diff --git a/src/options.go b/src/options.go index edd6f9a..b4afad7 100644 --- a/src/options.go +++ b/src/options.go @@ -408,6 +408,12 @@ func parseKeymap(keymap map[int]actionType, toggleSort bool, str string) (map[in keymap[key] = actToggleDown case "toggle-up": keymap[key] = actToggleUp + case "toggle-all": + keymap[key] = actToggleAll + case "select-all": + keymap[key] = actSelectAll + case "deselect-all": + keymap[key] = actDeselectAll case "toggle": keymap[key] = actToggle case "down": diff --git a/src/terminal.go b/src/terminal.go index 3dc0a75..d27c0d6 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -105,7 +105,10 @@ const ( actUnixWordRubout actYank actBackwardKillWord + actSelectAll + actDeselectAll actToggle + actToggleAll actToggleDown actToggleUp actDown @@ -661,20 +664,28 @@ func (t *Terminal) Loop() { } } } + selectItem := func(item *Item) bool { + if _, found := t.selected[item.index]; !found { + var strptr *string + if item.origText != nil { + strptr = item.origText + } else { + strptr = item.text + } + t.selected[item.index] = selectedItem{time.Now(), strptr} + return true + } + return false + } + toggleY := func(y int) { + item := t.merger.Get(y) + if !selectItem(item) { + delete(t.selected, item.index) + } + } toggle := func() { if t.cy < t.merger.Length() { - item := t.merger.Get(t.cy) - if _, found := t.selected[item.index]; !found { - var strptr *string - if item.origText != nil { - strptr = item.origText - } else { - strptr = item.text - } - t.selected[item.index] = selectedItem{time.Now(), strptr} - } else { - delete(t.selected, item.index) - } + toggleY(t.cy) req(reqInfo) } } @@ -725,11 +736,34 @@ func (t *Terminal) Loop() { t.input = append(t.input[:t.cx-1], t.input[t.cx:]...) t.cx-- } + case actSelectAll: + if t.multi { + for i := 0; i < t.merger.Length(); i++ { + item := t.merger.Get(i) + selectItem(item) + } + req(reqList, reqInfo) + } + case actDeselectAll: + if t.multi { + for i := 0; i < t.merger.Length(); i++ { + item := t.merger.Get(i) + delete(t.selected, item.index) + } + req(reqList, reqInfo) + } case actToggle: if t.multi && t.merger.Length() > 0 { toggle() req(reqList) } + case actToggleAll: + if t.multi { + for i := 0; i < t.merger.Length(); i++ { + toggleY(i) + } + req(reqList, reqInfo) + } case actToggleDown: if t.multi && t.merger.Length() > 0 { toggle() diff --git a/test/test_go.rb b/test/test_go.rb index 160699c..32677ad 100644 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -543,6 +543,29 @@ class TestGoFZF < TestBase assert_equal lines.last, `find . -print0 | #{FZF} --null -e -f "^#{lines.last}$"`.chomp end + def test_select_all_deselect_all_toggle_all + tmux.send_keys "seq 100 | #{fzf '--bind ctrl-a:select-all,ctrl-d:deselect-all,ctrl-t:toggle-all --multi'}", :Enter + tmux.until { |lines| lines[-2].include? '100/100' } + tmux.send_keys :BTab, :BTab, :BTab + tmux.until { |lines| lines[-2].include? '(3)' } + tmux.send_keys 'C-t' + tmux.until { |lines| lines[-2].include? '(97)' } + tmux.send_keys 'C-a' + tmux.until { |lines| lines[-2].include? '(100)' } + tmux.send_keys :Tab, :Tab + tmux.until { |lines| lines[-2].include? '(98)' } + tmux.send_keys 'C-d' + tmux.until { |lines| !lines[-2].include? '(' } + tmux.send_keys :Tab, :Tab + tmux.until { |lines| lines[-2].include? '(2)' } + tmux.send_keys 0 + tmux.until { |lines| lines[-2].include? '10/100' } + tmux.send_keys 'C-a' + tmux.until { |lines| lines[-2].include? '(12)' } + tmux.send_keys :Enter + assert_equal %w[2 1 10 20 30 40 50 60 70 80 90 100], readonce.split($/) + end + private def writelines path, lines File.unlink path while File.exists? path