New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add prefix arg for action functions #552

Closed
wants to merge 5 commits into
base: master
from

Conversation

Projects
None yet
2 participants
@jkitchin
Contributor

jkitchin commented Jun 12, 2016

This is a draft approach to enabling prefix args to be used for ivy actions. What do you think?

This commit makes ivy store prefix args that can be used in action
functions. The prefix arg is stored in `ivy-prefix-arg'.

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 |

Here are some examples of its behavior.

  • Tests of ivy-prefix-arg
    ** Tests with no prefix.
    These should all have a prefix arg of nil
    (require 'ivy-test)

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-m")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-j")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-M-j")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-M-m")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-M-n")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-M-p")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"M-o o")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"TAB TAB")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-' a")

** tests with one prefix
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix '(4))))
"C-u C-m")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix '(4))))
"C-u C-j")

;; this should not use prefix.
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-u C-M-j")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix '(4))))
"C-u C-M-m")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix '(4))))
"C-u C-M-n")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix '(4))))
"C-u C-M-p")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix '(4))))
"C-u M-o o")

; I am not sure if we can or need to make this work to pass prefix with TAB TAB. The prefix gets clobbered on the second tab I think.
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-u TAB TAB")

;this also does not seem to be doable. You can't enter the prefix in the avy selection, and it seems to get clobbered by C-'
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-u C-' a")

** Tests with a numeric prefix

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix 1)))
"C-u 1 C-m")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix 1)))
"C-u 1 C-j")

;; this should not pass a prefix.
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-u 1 C-M-j")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix 1)))
"C-u 1 C-M-m")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix 1)))
"C-u 1 C-M-n")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix 1)))
"C-u 1 C-M-p")

(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix 1)))
"C-u 1 M-o o")

; I am not sure if we can or need to make this work to pass prefix with TAB TAB. The prefix gets clobbered on the second tab I think.
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-u 1 TAB TAB")

;this also does not seem to be doable. You can't enter the prefix in the avy selection, and it seems to get clobbered by C-'
(ivy-with
'(progn (ivy-read "pattern: " '("blue" "yellow")
:action (lambda (x)
(setq test-prefix ivy-prefix-arg)))
(assert (equal test-prefix nil)))
"C-u 1 C-' a")

** 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-prefix-arg)
(elt x 0))
((equal '(4) ivy-prefix-arg)
(car (last x)))
(t
(elt x (prefix-numeric-value ivy-prefix-arg)))))))

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-prefix-arg'.

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 |

Here are some examples of its behavior.

* Tests of ivy-prefix-arg
** Tests with no prefix.
These should all have a prefix arg of nil
(require 'ivy-test)

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-m")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-j")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-M-j")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-M-m")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-M-n")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-M-p")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "M-o o")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "TAB TAB")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-' a")

** tests with one prefix
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix '(4))))
 "C-u C-m")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix '(4))))
 "C-u C-j")

;; this should not use prefix.
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-u C-M-j")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix '(4))))
 "C-u C-M-m")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix '(4))))
 "C-u C-M-n")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix '(4))))
 "C-u C-M-p")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix '(4))))
 "C-u M-o o")

; I am not sure if we can or need to make this work to pass prefix with TAB TAB. The prefix gets clobbered on the second tab I think.
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-u TAB TAB")

;this also does not seem to be doable. You can't enter the prefix in the avy selection, and it seems to get clobbered by C-'
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-u C-' a")

** Tests with a numeric prefix

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix 1)))
 "C-u 1 C-m")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix 1)))
 "C-u 1 C-j")

;; this should not pass a prefix.
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-u 1 C-M-j")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix 1)))
 "C-u 1 C-M-m")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix 1)))
 "C-u 1 C-M-n")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix 1)))
 "C-u 1 C-M-p")

(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix 1)))
 "C-u 1 M-o o")

; I am not sure if we can or need to make this work to pass prefix with TAB TAB. The prefix gets clobbered on the second tab I think.
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-u 1 TAB TAB")

;this also does not seem to be doable. You can't enter the prefix in the avy selection, and it seems to get clobbered by C-'
(ivy-with
 '(progn (ivy-read "pattern: " '("blue" "yellow")
		   :action (lambda (x)
			     (setq test-prefix ivy-prefix-arg)))
	 (assert (equal test-prefix nil)))
 "C-u 1 C-' a")

** 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-prefix-arg)
			   (elt x 0))
			  ((equal '(4) ivy-prefix-arg)
			   (car (last x)))
			  (t
			   (elt x (prefix-numeric-value ivy-prefix-arg)))))))
@jkitchin

This comment has been minimized.

Contributor

jkitchin commented Jun 12, 2016

I have signed FSF Emacs papers already, if you decide to accept these additions.

@abo-abo

This comment has been minimized.

Owner

abo-abo commented Jun 13, 2016

Why can't the action functions simply examine current-prefix-arg? What's the advantage of this approach?

@jkitchin

This comment has been minimized.

Contributor

jkitchin commented Jun 13, 2016

current-prefix-arg does not seem to be preserved across function calls. Without this patch, this:

#+BEGIN_SRC emacs-lisp
(ivy-read "pick: " '("blue" "yellow")
      :action (lambda (x) (message-box "%s" current-prefix-arg)))
#+END_SRC

reports current-prefix-arg to be nil no matter what you make it.

in contrast, with the patch,

#+BEGIN_SRC emacs-lisp
(ivy-read "pick: " '("blue" "yellow")
      :action (lambda (x) (message-box "%s" ivy-prefix-arg)))
#+END_SRC

reports the correct current prefix arg for me.

@abo-abo

This comment has been minimized.

Owner

abo-abo commented Jun 13, 2016

I see, thanks. Could you please rename ivy-prefix-arg to ivy-current-prefix-arg, since it should mirror the built-in current-prefix-arg and not the built-in prefix-arg?

Also, move the tests from the commit message to ivy-test.el in the following manner:

(ert-deftest ivy-prefix-arg ()
    (should (equal
             (ivy-with
              '(let (res)
                (ivy-read "pattern: " '("blue" "yellow")
                 :action (lambda (x)
                           (setq res ivy-prefix-arg)))
                res)
              "M-1 M-2 M-3 C-m")
             123)))

Update the PR afterwards, force-push is fine.

jkitchin added some commits Jun 12, 2016

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.
merge
Comment out some tests that don't pass.
@jkitchin

This comment has been minimized.

Contributor

jkitchin commented Jun 13, 2016

I think I have updated the PR now. I commented out 3 tests which don't work in the batch tests. I think one fails because avy is not loaded, and the one with "M-o o" fails with a stringp error. I am not sure what the best way to handle those are.

@abo-abo abo-abo closed this in e54aa18 Jun 13, 2016

@abo-abo

This comment has been minimized.

Owner

abo-abo commented Jun 13, 2016

Thanks, merged everything except the two avy tests. I haven't set up any kind of dependency resolving here to keep things simple. I think it's good enough even some avy or multiple-cursors stuff isn't tested.

@jkitchin

This comment has been minimized.

Contributor

jkitchin commented Jun 13, 2016

great, thanks!

Oleh Krehel writes:

Thanks, merged everything except the two avy tests. I haven't set up any kind of dependency resolving here to keep things simple. I think it's good enough even some avy or multiple-cursors stuff isn't tested.


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub:
#552 (comment)

Professor John Kitchin
Doherty Hall A207F
Department of Chemical Engineering
Carnegie Mellon University
Pittsburgh, PA 15213
412-268-7803
@johnkitchin
http://kitchingroup.cheme.cmu.edu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment