Como posso verificar se um usuário pressionou um botão enquanto uma função está em execução?

8

Eu tenho uma função que é executada automaticamente depois que o usuário executa alguma ação. No entanto, a função leva muito tempo para ser concluída. Isso significa que o Emacs fica sem resposta por um curto período de tempo após cada entrada. Para corrigir isso, gostaria de verificar se o usuário inseriu uma nova entrada e, em caso afirmativo, cancelar a execução desta função e prosseguir com a entrada do usuário.

Existe uma variável que contém entradas de teclado na fila? Caso contrário, existe outra maneira de verificar se o usuário pressionou um botão desde que uma função começou a ser executada?

jcaw
fonte
2
Você pode querer olhar para a while-no-inputmacro. Parece fazer o que você deseja: isto é, cancela a execução de seu corpo quando novas entradas são exibidas.
Wvxvw 04/11
@wvxvw Seu comentário me parece uma boa resposta. Você deve publicá-lo como tal!
Phil Hudson

Respostas:

5

OK, vou tentar uma resposta expandida. O problema é que o Emacs Lisp é de thread único. Você pode girar mais processos, mas não dentro do interpretador Emacs Lisp. No entanto, a entrada do teclado que você precisa ler do usuário para interromper sua função precisa ser processada pelo mesmo intérprete do Emacs Lisp. Isso significa que, se o seu intérprete estiver travado na interpretação de algum código Lisp, talvez não haja uma maneira de interrompê-lo por dentro. Aqui está um lugar no manual que descreve essa situação:

No nível do código C, desistir não pode acontecer em qualquer lugar; somente nos locais especiais que verificam a bandeira de abandono. A razão para isso é que parar em outros lugares pode deixar uma inconsistência no estado interno do Emacs. Como o encerramento é adiado até um local seguro, o encerramento não pode causar um crash no Emacs.

https://www.gnu.org/software/emacs/manual/html_node/elisp/Quitting.html

Então, como o Emacs ainda tem a C-gfuncionalidade / canceling? - Existem timers e funções que executam E / S (aguardam eventos no loop de comando). Os temporizadores podem organizar seus cálculos em pedaços, para que você tenha pontos em seus cálculos em que possa olhar ao redor e, se necessário, impedir todos os cálculos adicionais. As funções de E / S podem enviar quitsinal. A macro with-keyboard-quitaguarda esse sinal e, uma vez recebido, sai normalmente. No entanto, sua função precisa saber para enviar este sinal. Isso significa que, se sua função impedir que o usuário envie entrada do teclado, você não poderá se beneficiar das interrupções do teclado.

Conclusão: tente agrupar o código da sua função while-no-input. Se isso não funcionar, tente reescrever sua função de forma que o Emacs processe os eventos do teclado.

wvxvw
fonte
C-gsempre pode interromper o código lisp, while-no-inputapenas o estende a outras entradas do teclado.
npostavs
@npostavs não é verdade. Tente interromper algo parecido (while t). (Mas antes de fazer isso, verifique se todas as alterações foram salvas).
Wvxvw
Eu digitei (while t)em *scratch*e bateu C-x C-eentão C-g; Eu recebi a Quitmensagem, o Emacs não ficou preso.
npostavs
@npostavs bem, certamente encontrarei uma combinação que não é possível interromper, mas não temos tempo para procurá-la agora. Não tenho certeza de quanto *scratch*pode ser confiável, porque cada avaliação que você faz lá é envolvida em uma função auxiliar.
Wvxvw
Eu consegui ficar preso (while t (condition-case _ (while t) (quit nil))), eu acho que na teoria é interrompível se você pudesse acertar C-gno momento certo, mas na prática você não pode.
npostavs
3

Eu iria com input-pending-t. Dos documentos:

(entrada-pendente-p e opcionais CHECK-TIMERS)

Retorne t se a entrada do comando estiver disponível no momento sem espera. Na verdade, o valor é nulo apenas se pudermos ter certeza de que nenhuma entrada está disponível; se houver dúvida, o valor é t.

Um exemplo rápido:

(progn
  ;; I put this into a progn call so you can call it direcly from Emacs
  ;; if you are using the sx package
  (message "Try pressing a key in the next 2 seconds!")
  (sleep-for 2)
  (message (if (input-pending-p)
               "There is input waiting!"
             "No input, let’s move on!"))
  (sleep-for 2))
GergelyPolonkai
fonte
Perfeito! Ambas as respostas são ótimas, pena que eu só poderia aceitar uma. Obrigado!
jcaw