reddit / emacs
how to bind a command with a prefix to a key
dm_g · 2022-06-25 19:45
I am sorry if this is a stupid question, but I am having trouble finding an answer.
I have a command I want to bind to a key, let us call it do-something
do-something can be called with a prefix: one ctrl-u and two ctrl-u, it then changes its behaviour
I want to avoid having to type the ctrl-u(s). How do I do it?
I guess I can a lambda that calls do-something with the prefix. But I do not know how to programmatically call do-something as if it was called with a prefix.
Any help would be appreciated.
kaushalmodi · 2022-06-25 20:33
It's called the `current-prefix-arg` ([ref](https://www.gnu.org/software/emacs/manual/html_node/elisp/Prefix-Command-Arguments.html)). You can let-bind that variable to `'(4)` to mimic `C-u` prefix, or `'(16)` for `C-u C-u`.
ieure · 2022-06-25 21:10
You can go that route, but most commands accept normal function arguments, for example:
(defun beginning-of-defun (&optional arg) ... )
While you *could* do:
(let ((current-prefix-arg '(16)) (beginning-of-defun))
It's much simpler (and clearer) to supply the argument explicitly:
(beginning-of-defun 16)
Most commands work this way, and it should be preferred over let-binding `current-prefix-arg` where possible (which is the vast majority of cases).
lastnamebird · 2022-06-25 22:38
Would it be more accurate to have `(beginning-of-define ‘(16))`? Is the prefix arg modified when read by a function?
Also, since, as you say, only some commands accept the `prefix-arg`as a parameter, the let binding would still be more robust, working in all cases.
dm_g · 2022-06-25 23:48
Thank you both the responses, but it seems to be more complicate that this. The function I am trying to wrap has one parameter and two optional parameters (its source is attached below, from the web-search package, see below) .
\- If the function does not get a query it asks for one.
\- the c-u is used to prompt from a list of predefined values (the providers). c-u c-u does the same from predefined tags.
I can't call it with the optional parameter, because it will complain that I am missing the query parameter.
I tried to wrap it:
(defun web-search-all ()
(interactive)
(let ((current-prefix-arg '(4)))
(web-search)))
I get the same error "wrong number of arguments".
what I want is to avoid having to type C-u C-u before calling web-search, and instead having a new function that I can call directly without the extra (one or two extra c-u).
(defun web-search (query &optional providers tag)
"Search for QUERY on website(s).
Without prefix argument, search on `web-search-default-provider'.
One C-u, choose a provider.
Two C-u, choose a tag (notes that a tag can match multiple providers)."
(interactive
(let* ((providers
(if (equal current-prefix-arg '(4)) ; One C-u
(let* ((default web-search-default-provider)
(prompt (format "Provider (default %s): " default))
(completion-ignore-case t))
(list (completing-read prompt web-search-providers nil t nil nil default)))
(list web-search-default-provider)))
(tag
(when (equal current-prefix-arg '(16)) ; Two C-u
(completing-read "Tag: " (web-search--tags))))
(query
(let ((initial (if (use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end))
(current-word)))
(prompt (format "Search %s: "
(if tag
(mapconcat #'identity
(mapcar #'car (web-search--find-providers tag))
", ")
(mapconcat #'identity providers ", ")))))
(read-string prompt initial))))
(list query providers tag)))
(setq providers (or (and tag (web-search--find-providers tag))
providers
(list web-search-default-provider)))
(mapc #'browse-url (web-search--format-urls query providers)))
ieure · 2022-06-26 00:00
> Would it be more accurate to have (beginning-of-define ‘(16))?
No, that would signal an error. Commands can specify that they want a raw prefix argument, which passes the cons-cell `'(16)` form, or that they want it converted to a number, which passes `16`. The `beginning-of-defun` command specifies that it wants a number, so you must pass it a number when calling.
Specifying the raw prefix lets commands distinguish between a `C-u` prefix and a `C-4` prefix. Most don't need that.
> Is the prefix arg modified when read by a function?
I don't really understand what you're asking.
> Also, since, as you say, only some commands accept the prefix-argas a parameter, the let binding would still be more robust, working in all cases.
The overwhelming majority accept arguments normally, and commands are *generally* intended to be invoked by a key binding, not called from other functions, so in practice, it isn't a problem. There's no need to use a weird calling convention for 100% of calls when it's only needed for 0.01%.
ieure · 2022-06-26 00:04
The function *requires* a `query` argument, which you aren't providing; that's why you get the error. When you invoke it with a keybinding, the forms inside `(interactive …)` are evaluated (which prompts you), and the list it returns is used as the argument list. It doesn't get evaluated when invoking as a function.
Try this:
(defun web-search-all ()
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively #'web-search)))
kaushalmodi · 2022-06-26 00:43
You'll need to do `(call-interactively #'web-search)`, not a plain elisp call.
**Update** nevermind, I saw that /u/ieure got that covered :)
dpoggio · 2022-06-26 03:26
I haven’t read all of the comments, but it seems many of you think about prefix as a parameter. It’s not. Call the function with regular parameters, and change the VARIABLE called current-prefix-arg.
lastnamebird · 2022-06-26 03:33
I was confused by your initial example which does not actually work. `beginning-of-defun` doesn't seem to read the `current-prefix-arg` at all if called programmatically.
To your point, most interactive commands don't read the `current-prefix-arg` variable if not called interactively, so passing a direct value is the only way to get them to work.
I appreciate the info, I did learn something new and useful going forward.
`
ieure · 2022-06-26 03:46
You're right, it should have been:
(let ((current-prefix-arg '(16)))
(call-interactively #'beginning-of-defun))
dm_g · 2022-06-26 19:39
>Specifying the raw prefix lets commands distinguish between a
>
>C-u
>
> prefix and a
>
>C-4
>
> prefix. Most don't need that.
Thank you ierure, and everybody elese. This thread has been very educational.