From 5a7b41a2cf1271537c9831b0fddd38dbe58eb60d Mon Sep 17 00:00:00 2001
From: Junegunn Choi <junegunn.c@gmail.com>
Date: Sat, 2 Dec 2017 02:27:02 +0900
Subject: [PATCH] Add accept-non-empty action

'accept-non-empty' is similar to 'accept' (which is bound to 'enter' and
'double-click' by default) but it prevents fzf from exiting without any
selection.

Close #1162
---
 man/man1/fzf.1  |  1 +
 src/options.go  |  2 ++
 src/terminal.go |  5 +++++
 test/test_go.rb | 36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 44 insertions(+)

diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 301e2099..60012501 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -492,6 +492,7 @@ triggered whenever the query string is changed.
   \fBACTION:               DEFAULT BINDINGS (NOTES):
     \fBabort\fR                 \fIctrl-c  ctrl-g  ctrl-q  esc\fR
     \fBaccept\fR                \fIenter   double-click\fR
+    \fBaccept-non-empty\fR      (same as \fBaccept\fR except that it prevents fzf from exiting without selection)
     \fBbackward-char\fR         \fIctrl-b  left\fR
     \fBbackward-delete-char\fR  \fIctrl-h  bspace\fR
     \fBbackward-kill-word\fR    \fIalt-bs\fR
diff --git a/src/options.go b/src/options.go
index 730c1167..160fdd01 100644
--- a/src/options.go
+++ b/src/options.go
@@ -662,6 +662,8 @@ func parseKeymap(keymap map[int][]action, str string) {
 				appendAction(actAbort)
 			case "accept":
 				appendAction(actAccept)
+			case "accept-non-empty":
+				appendAction(actAcceptNonEmpty)
 			case "print-query":
 				appendAction(actPrintQuery)
 			case "replace-query":
diff --git a/src/terminal.go b/src/terminal.go
index 82f0ac9f..6881cdb5 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -170,6 +170,7 @@ const (
 	actBeginningOfLine
 	actAbort
 	actAccept
+	actAcceptNonEmpty
 	actBackwardChar
 	actBackwardDeleteChar
 	actBackwardWord
@@ -1656,6 +1657,10 @@ func (t *Terminal) Loop() {
 				req(reqList)
 			case actAccept:
 				req(reqClose)
+			case actAcceptNonEmpty:
+				if len(t.selected) > 0 || t.merger.Length() > 0 || !t.reading && t.count == 0 {
+					req(reqClose)
+				}
 			case actClearScreen:
 				req(reqRedraw)
 			case actTop:
diff --git a/test/test_go.rb b/test/test_go.rb
index eca458ca..03fee238 100644
--- a/test/test_go.rb
+++ b/test/test_go.rb
@@ -1378,6 +1378,42 @@ class TestGoFZF < TestBase
     tmux.send_keys :Enter
   end
 
+  def test_accept_non_empty
+    tmux.send_keys %(seq 1000 | #{fzf '--print-query --bind enter:accept-non-empty'}), :Enter
+    tmux.until { |lines| lines.match_count == 1000 }
+    tmux.send_keys 'foo'
+    tmux.until { |lines| lines[-2].include? '0/1000' }
+    # fzf doesn't exit since there's no selection
+    tmux.send_keys :Enter
+    tmux.until { |lines| lines[-2].include? '0/1000' }
+    tmux.send_keys 'C-u'
+    tmux.until { |lines| lines[-2].include? '1000/1000' }
+    tmux.send_keys '999'
+    tmux.until { |lines| lines[-2].include? '1/1000' }
+    tmux.send_keys :Enter
+    assert_equal %w[999 999], readonce.split($INPUT_RECORD_SEPARATOR)
+  end
+
+  def test_accept_non_empty_with_multi_selection
+    tmux.send_keys %(seq 1000 | #{fzf '-m --print-query --bind enter:accept-non-empty'}), :Enter
+    tmux.until { |lines| lines.match_count == 1000 }
+    tmux.send_keys :Tab
+    tmux.until { |lines| lines[-2].include? '1000/1000 (1)' }
+    tmux.send_keys 'foo'
+    tmux.until { |lines| lines[-2].include? '0/1000' }
+    # fzf will exit in this case even though there's no match for the current query
+    tmux.send_keys :Enter
+    assert_equal %w[foo 1], readonce.split($INPUT_RECORD_SEPARATOR)
+  end
+
+  def test_accept_non_empty_with_empty_list
+    tmux.send_keys %(: | #{fzf '-q foo --print-query --bind enter:accept-non-empty'}), :Enter
+    tmux.until { |lines| lines[-2].strip == '0/0' }
+    tmux.send_keys :Enter
+    # fzf will exit anyway since input list is empty
+    assert_equal %w[foo], readonce.split($INPUT_RECORD_SEPARATOR)
+  end
+
   def test_preview_update_on_select
     tmux.send_keys(%(seq 10 | fzf -m --preview 'echo {+}' --bind a:toggle-all),
                    :Enter)
-- 
GitLab