This page shows you how to make your emacs lisp command accept universal-argument 【Ctrl+u】 given by user.
You have written a emacs command. You want the command's behavior to be slightly different if user presses 【Ctrl+u】 before calling your command.
Emacs has a mechanism for a command to have variant behavior if user calls universal-argument 【Ctrl+u】.
The universal argument has many uses. It can let user repeat a command n times. For example, type 【Ctrl+u 20 -】 and it'll type out --------------------.
For some commands, the universal argument is useful for variant behavior of the command. For example, in dired, typing w will copy the file name (dired-copy-filename-as-kill), but if you type 【Ctrl+u 0 w】, the copied name will be file full path.
To make your command aware of universal argument, there are 2 major ways:
(interactive "P") to your function. It will pass the value of current-prefix-arg to your function's first argument.Example:
(defun f (x) … (interactive "P") ;; value of x is from universal argument, or nil if universal-argument isn't called )
The following table shows the possible values of current-prefix-arg.
| Key Input | Value of current-prefix-arg |
|---|---|
| No universal arg called. | nil |
| 【Ctrl+u -】 | Symbol - |
| 【Ctrl+u - 2】 | Number -2 |
| 【Ctrl+u 1】 | Number 1 |
| 【Ctrl+u 4】 | Number 4 |
| 【Ctrl+u】 | List '(4) |
| 【Ctrl+u Ctrl+u】 | List '(16) |
(info "(elisp) Prefix Command Arguments")
Let's say you want a command to copy the file path of current file. But if universal-argument is called, then copy just the dir path. Here's how you code it:
(defun copy-file-path (&optional dirPathOnly-p) "Copy the current buffer's file path or dired path to `kill-ring'. If `universal-argument' is called, copy only the dir path." (interactive "P") (let ((fPath (if (equal major-mode 'dired-mode) default-directory (buffer-file-name) ))) (kill-new (if (equal dirPathOnly-p nil) fPath (file-name-directory fPath) ))) (message "File path copied.") )
First, we set fPath. Then, we check “dirPathOnly-p”. If it's nil, we don't do anything special, else, we truncate the file path to just the dir.
Sometimes you do not want your function's parameter spec to explicitly contain universal argument, because it is not relevant to the function. So, (interactive "P") is not appropriate. For example, suppose you are writing this command:
(defun wrap-html-tag (tagName &optional className id) "Add a HTML tag to beginning and ending of current word or text selection." )
Your command will create a HTML tag like this:
cat ↓ <div>cat</div> or <div class="xyz">cat</div> or <div id="id8295" class="xyz">cat</div>
When called interactively, it can prompt user to enter “class” and “id” values. If user just press Enter ↵ without giving them any value, then don't add these attributes to the tag.
However, often, the prompting for “class” and “id” are annoying, because, in practice, many tags don't need them. ⁖ {<b>bold</b>, <i>italic</i>}.
So, you want your command to do extra prompt only when preceded by universal-argument.
Here's solution:
(defun wrap-html-tag (tagName &optional className ξid) "Add a HTML tag to beginning and ending of current word or text selection. When preceded with `universal-argument', no arg = prompt for tag, class. 2 = prompt for tag, id. any = prompt for tag, id, class. When called interactively, Default id value is 「id‹random number›」. Default class value is 「xyz」. When called in lisp program, if className is nil or empty string, don't add the attribute. Same for ξid." (interactive (cond ((equal current-prefix-arg nil) ; universal-argument not called (list (read-string "Tag (span):" nil nil "span") )) ((equal current-prefix-arg '(4)) ; C-u (list (read-string "Tag (span):" nil nil "span") (read-string "Class (xyz):" nil nil "xyz") )) ((equal current-prefix-arg 2) ; C-u 2 (list (read-string "Tag (span):" nil nil "span") (read-string "id:" nil nil (format "id%d" (random (expt 2 28 )))) )) (t ; all other cases (list (read-string "Tag (span):" nil nil "span") (read-string "Class (xyz):" nil nil "xyz") (read-string "id:" nil nil (format "id%d" (random (expt 2 28 )))) )) ) ) ;; now, all the parameters of your function is filled. ;; code body here )
The (interactive …) is used to fill out the parameters, when your function is called by user interactively (as opposed to from a lisp program).
One way to use the interactive function is for it to return a list. This list's element will be fed to the function as arguments.
In our code, we use a conditional (cond …) to check the values of current-prefix-arg. If it's nil, then that means universal-argument is not called. So, we simply prompt for the tag name. But if the value is other, then we prompt for more.
The weird ξ you see in my elisp code is Greek x. I use Unicode char in symbol name for easy distinction from builtin symbols. You can just ignore it. 〔☛ Programing Style: Variable Naming: English Words Considered Harmful〕