¿Cómo puedo leer un solo personaje del minibúfer?

12

Cuando parte de un defun,

(interactive "c(C)hoose (A)n (O)ption")

le pedirá al usuario un solo carácter; RETno es requerido. ¿Cómo puedo replicar este comportamiento de lectura sin la necesidad de hacerlo interactive?

Sean Allred
fuente

Respuestas:

7

En lugar de lo read-charque recomiendo read-key. La diferencia es que read-keyobedece todas las reasignaciones habituales como input-decode-mapy function-key-map, por lo que funcionará correctamente en un momento.

Stefan
fuente
Junto con la información en otra respuesta , esta parece ser la respuesta más precisa a la pregunta formulada :) Sin embargo, el comentario de glucas proporciona una buena función :)read-char-choice
Sean Allred
5

Además de las formas integradas para leer eventos individuales como read-chary read-char-exclusive, aquí hay una opción para leer un solo carácter, pero también especificar qué caracteres son de entrada aceptable:

(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)))

Por lo tanto, todo lo siguiente aceptará "C", "A" u "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))

Y aquí hay una forma de ejemplo de bucle para la entrada correcta en una responsevariable:

(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)
Dan
fuente
2
También hay read-char-choiceuno que lee uno de un conjunto de caracteres dado.
glucas
@glucas: ah, loco, tienes razón. Parece que reinventé la rueda.
Dan
4

call-interactivelyes lo que interpreta la (interactive "cPROMPT")especificación, la copción se envía a read-char. Por lo tanto, lo siguiente debería funcionar en un contexto no interactivo:

(read-char "(C)hoose (A)n (O)ption")
wasamasa
fuente
3

La pregunta fue respondida hace mucho tiempo, pero esta respuesta adicional puede proporcionar alguna ayuda a otros buscadores.

read-char-choicele permite especificar una lista de opciones. El fn no volverá hasta que el usuario seleccione una de esas opciones válidas.

(read-char-choice "prompt here (A, B, or C)? " '(?A ?B ?C))

En el caso degenerado en el que las opciones son simplemente Y o N (no distingue entre mayúsculas y minúsculas), existe y-or-n-p.

Ambos read-char-choicey y-or-n-pson rígidos, e insisten en una respuesta válida. En el primer caso, debe ser una de las opciones que especifique (como A, B o C en mi ejemplo), y en el último caso, debe ser Y o N. Si el usuario presiona Intro o cualquier otra tecla, y-or-n-pvolverá a preguntar. El read-char-choicesolo se sentará allí, en silencio. Ninguno de los dos proporciona una manera de devolver un valor predeterminado. Para obtener ese comportamiento, creo que debes construir tu propia interacción con read-charo read-key.

En mi experiencia, el problema con read-chary read-keysolo es que, si bien muestran la solicitud en el minibúfer, el cursor permanece en el búfer de edición principal. Esto es desorientador para el usuario y también es diferente al comportamiento de read-string.

Para evitar ESO, puede dejar que la variable cursor-in-echo-areaantes de llamar read-keymuestre el cursor en el minibúfer.

(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)))
Cheeso
fuente