When part of a defun
,
(interactive "c(C)hoose (A)n (O)ption")
will prompt the user for a single character; RET
is not required. How can I replicate this reading behavior without the need for interactive
?
When part of a defun
,
(interactive "c(C)hoose (A)n (O)ption")
will prompt the user for a single character; RET
is not required. How can I replicate this reading behavior without the need for interactive
?
Rather than read-char
I recommend read-key
. The difference is that read-key
obeys all the usual remappings such as input-decode-map
and function-key-map
, so it will work properly in a tty.
In addition to built-in ways to read single events such as read-char
and read-char-exclusive
, here's an option to read a single character, but also specify which characters are acceptable input:
(defun read-char-picky (prompt chars &optional inherit-input-method seconds)
"Read characters like in `read-char-exclusive', but if input is
not one of CHARS, return nil. CHARS may be a list of characters,
single-character strings, or a string of characters."
(let ((chars (mapcar (lambda (x)
(if (characterp x) x (string-to-char x)))
(append chars nil)))
(char (read-char-exclusive prompt inherit-input-method seconds)))
(when (memq char chars)
char)))
So all of the following will accept either "C", "A", or "O":
(read-char-picky "(C)hoose (A)n (O)ption: " "CAO")
(read-char-picky "(C)hoose (A)n (O)ption: " '("C" "A" "O"))
(read-char-picky "(C)hoose (A)n (O)ption: " '(?C ?A ?O))
And here's an example way to loop for the correct input into a response
variable:
(let (response)
(while (null (setq response
(read-char-picky "(C)hoose (A)n (O)ption: " "CAO")))
(message "Please pick one of \"C\", \"A\", or \"O\"!")
(sit-for .5))
response)
The question was answered long ago, but this additional answer may provide some assistance to other searchers.
read-char-choice
allows you to specify a list of choices. The fn will not return until the user selects one of those valid options.
(read-char-choice "prompt here (A, B, or C)? " '(?A ?B ?C))
In the degenerate case in which the options are simply Y or N (case insenstive), there is y-or-n-p
.
Both read-char-choice
and y-or-n-p
are rigid, and insist on a valid answer. In the former case, it must one of the options you specify (like A, B or C in my example), and in latter case, it must be Y or N . If the user presses enter or any other key, the y-or-n-p
will ask again. The read-char-choice
will just sit there, silent. Neither provides a way to just return a default. To get that behavior, I think you've got to build your own interaction with read-char
or read-key
.
In my experience, the problem with read-char
and read-key
alone, is that while they display the prompt in the minibuffer, the cursor remains in the main editing buffer. This is disorienting to the user, and is also unlike the behavior of read-string
.
To avoid THAT, you can let the variable cursor-in-echo-area
prior to calling read-key
to display the cursor in the minibuffer.
(defun my-y-or-n-with-default (raw-prompt &optional default-yes)
"displays PROMPT in the minibuffer, prompts for a y or n,
returns t or nil accordingly. If neither Y or N is entered, then
if DEFAULT-YES, returns t, else nil."
(let* ((options-string (if default-yes "Y or n" "y or N"))
(prompt (concat raw-prompt "(" options-string ")? "))
(cursor-in-echo-area t)
(key (read-key (propertize prompt 'face 'minibuffer-prompt)))
(isyes (or (eq key ?y) (eq key ?Y)))
(isno (or (eq key ?n) (eq key ?N))))
(if (not (or isyes isno))
default-yes
isyes)))
call-interactively
is what interprets the (interactive "cPROMPT")
specification, the c
option is dispatched to read-char
. Therefore, the following should work in a non-interactive context:
(read-char "(C)hoose (A)n (O)ption")