branch: master
commit e54aa1850077d22e8007edef86d4bb751c3b7794
Author: John Kitchin <[email protected]>
Commit: Oleh Krehel <[email protected]>
Add prefix arg for action functions
This commit makes ivy store prefix args that can be used in action
functions. The prefix arg is stored in `ivy-current-prefix-arg'.
New tests are added to ivy-test.el.
Fixes #552
Single actions
| C-m | ivy-done | store prefix |
| M-o | ivy-dispatching-done | store prefix |
| C-j | ivy-alt-done | store prefix |
| C-M-j | ivy-immediate-done | store prefix |
| TAB TAB | ivy-partial-or-done | unsupported |
| C-' | ivy-avy | unsupported |
Multiple actions
| C-M-m | ivy-call | store prefix, type prefix again for
next call |
| C-M-o | ivy-dispatching-call | store prefix, type prefix again for
next call |
| C-M-n | ivy-next-line-and-call | store prefix, type prefix again for
next call |
| C-M-p | ivy-previous-line-and-call | store prefix, type prefix again for
next call |
** An example application
no prefix prints first number in a message-box
one prefix prints last number in a message-box
numeric prefix selects the index to print in a message-box
(ivy-read "choose: " '(("a" 1 2 3)
("b" 3 4 5))
:action
(lambda (x)
(message-box "%s"
(cond
((null ivy-current-prefix-arg)
(elt x 0))
((equal '(4) ivy-current-prefix-arg)
(car (last x)))
(t
(elt x (prefix-numeric-value
ivy-current-prefix-arg)))))))
---
ivy-test.el | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ivy.el | 21 ++++++-
2 files changed, 209 insertions(+), 2 deletions(-)
diff --git a/ivy-test.el b/ivy-test.el
index dee8803..a0ccb9c 100644
--- a/ivy-test.el
+++ b/ivy-test.el
@@ -212,4 +212,194 @@
0.07058823529411765
0.20392156862745098))))
+
+;;* prefix arg tests
+;;** tests with no prefix
+(ert-deftest ivy-no-prefix-arg ()
+ "Tests with no prefix arg."
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-m")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-j")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-M-j")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-M-m")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-M-n")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-M-p")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "M-o o")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "TAB TAB")
+ nil)))
+
+;;** tests with one prefix
+(ert-deftest ivy-one-prefix-arg ()
+ "Tests with no prefix arg."
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u C-m")
+ '(4)))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u C-j")
+ '(4)))
+ ;; C-M-j does not pass a prefix on.
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u C-M-j")
+ nil))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u C-M-m")
+ '(4)))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u C-M-n")
+ '(4)))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u C-M-p")
+ '(4)))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u M-o o")
+ '(4)))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action
+ '(1 ("o" (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ ("p" (lambda (x)
+ (setq res ivy-current-prefix-arg)))))
+ res)
+ "C-u M-o p")
+ '(4)))
+ ;; TAB TAB does not pass prefix arg
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "TAB TAB")
+ nil)))
+
+
+(ert-deftest ivy-numeric-prefix-arg ()
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "M-1 M-2 M-3 C-m")
+ 123))
+ (should (equal
+ (ivy-with
+ '(let (res)
+ (ivy-read "pattern: " '("blue" "yellow")
+ :action (lambda (x)
+ (setq res ivy-current-prefix-arg)))
+ res)
+ "C-u 123 C-m")
+ 123)))
+
+
(provide 'ivy-test)
diff --git a/ivy.el b/ivy.el
index 76d12fa..d882999 100644
--- a/ivy.el
+++ b/ivy.el
@@ -227,6 +227,11 @@ Example:
(setq ivy--sources-list
(plist-put ivy--sources-list cmd sources)))
+(defvar ivy-current-prefix-arg nil
+ "Prefix arg to pass to actions.
+This is a global variable that is set by ivy functions for use in
+action functions.")
+
;;* Keymap
(require 'delsel)
(defvar ivy-minibuffer-map
@@ -466,6 +471,7 @@ When non-nil, it should contain at least one %d.")
(defun ivy-done ()
"Exit the minibuffer with the selected candidate."
(interactive)
+ (setq ivy-current-prefix-arg current-prefix-arg)
(delete-minibuffer-contents)
(cond ((or (> ivy--length 0)
;; the action from `ivy-dispatching-done' may not need a
@@ -544,10 +550,11 @@ selection, non-nil otherwise."
(defun ivy-dispatching-call ()
"Select one of the available actions and call `ivy-call'."
(interactive)
+ (setq ivy-current-prefix-arg current-prefix-arg)
(let ((actions (copy-sequence (ivy-state-action ivy-last))))
(unwind-protect
- (when (ivy-read-action)
- (ivy-call))
+ (when (ivy-read-action)
+ (ivy-call))
(ivy-set-action actions))))
(defun ivy-build-tramp-name (x)
@@ -566,6 +573,7 @@ Is is a cons cell, related to
`tramp-get-completion-function'."
"Exit the minibuffer with the selected candidate.
When ARG is t, exit with current text, ignoring the candidates."
(interactive "P")
+ (setq ivy-current-prefix-arg current-prefix-arg)
(cond (arg
(ivy-immediate-done))
(ivy--directory
@@ -909,6 +917,15 @@ Example use:
(defun ivy-call ()
"Call the current action without exiting completion."
(interactive)
+ (unless
+ (or
+ ;; this is needed for testing in ivy-with which seems to call ivy-call
+ ;; again, and this-command is nil in that case.
+ (null this-command)
+ (memq this-command '(ivy-done
+ ivy-alt-done
+ ivy-dispatching-done)))
+ (setq ivy-current-prefix-arg current-prefix-arg))
(unless ivy-inhibit-action
(let ((action (ivy--get-action ivy-last)))
(when action