Como posso ler um único caractere do minibuffer?

12

Quando parte de um defun,

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

solicitará ao usuário um único caractere; RETnão é necessário. Como posso replicar esse comportamento de leitura sem a necessidade interactive?

Sean Allred
fonte

Respostas:

7

Em vez de read-charrecomendar read-key. A diferença é que read-keyobedece a todos os remapeamentos usuais, como input-decode-mape function-key-map, para que funcione corretamente em um tty.

Stefan
fonte
Emparelhado com as informações em outra resposta , esta parece ser a resposta mais precisa à pergunta :) O comentário de glucas fornece uma função boa embora :)read-char-choice
Sean Allred
5

Além das maneiras internas de ler eventos únicos como read-chare read-char-exclusive, aqui está uma opção para ler um único caractere, mas também especificar quais caracteres são aceitáveis ​​de entrada:

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

Portanto, todos os itens a seguir aceitarão "C", "A" ou "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))

E aqui está um exemplo de maneira de fazer o loop da entrada correta em uma responsevariável:

(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
fonte
2
Há também o read-char-choiceque lê um de um determinado conjunto de caracteres.
glucas
@ glucas: ah, nozes, você está certo. Parece que eu reinventei a roda.
Dan
4

call-interactivelyé o que interpreta a (interactive "cPROMPT")especificação, a copção é despachada para read-char. Portanto, o seguinte deve funcionar em um contexto não interativo:

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

A pergunta foi respondida há muito tempo, mas essa resposta adicional pode ajudar alguns outros usuários.

read-char-choicepermite especificar uma lista de opções. O fn não retornará até que o usuário selecione uma dessas opções válidas.

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

No caso degenerado em que as opções são simplesmente Y ou N (não diferencia maiúsculas de minúsculas), existe y-or-n-p.

Ambos read-char-choicee y-or-n-psão rígidas, e insistem em uma resposta válida. No primeiro caso, ele deve ser uma das opções especificadas (como A, B ou C no meu exemplo) e, no último caso, deve ser Y ou N. Se o usuário pressionar enter ou qualquer outra tecla, a y-or-n-psolicitação será novamente. O read-char-choicevai apenas ficar lá, em silêncio. Nenhum dos dois fornece uma maneira de retornar apenas um padrão. Para obter esse comportamento, acho que você precisa criar sua própria interação com read-charou read-key.

Na minha experiência, o problema com read-chare read-keysozinho é que, enquanto eles exibem o prompt no minibuffer, o cursor permanece no buffer de edição principal. Isso é desorientador para o usuário e também é diferente do comportamento de read-string.

Para evitar isso, você pode deixar a variável cursor-in-echo-areaantes da chamada read-keyexibir o cursor no 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)))
Cheeso
fonte