¿Cómo puedo simular un evento de clave arbitraria de Elisp?

26

¿Es posible simular un evento clave arbitrario desde elisp? Soy consciente de las formas en que puedo encontrar el enlace para una clave determinada y luego llamar a ese comando de forma interactiva, pero ¿qué sucede si ese evento clave no está vinculado a un comando?

Como ejemplo , ¿qué pasa si quisiera vincularme C-`para comportarme igual que la ESCclave en todos los contextos ?

nispio
fuente
Parece que key-bindingses la etiqueta incorrecta si no está tratando de alias un enlace de clave. Además, tal vez debería cambiar su ejemplo por otro para que no se confunda.
b4hand
@ b4hand Estoy abierto a sugerencias para mejores etiquetas. No hay key-eventsetiqueta ¿Debo hacer uno?
nispio
Me parece razonable, pero los eventos podrían ser mejores ya que esto también podría ser aplicable a los eventos del mouse.
b4hand
2
Todavía estoy confundido acerca de si desea simular un evento clave en elisp, o si desea específicamente la capacidad de hacer que una clave actúe como si fuera otra clave. Los gustos de key-translation-mapfacilitar este último, por lo que si eso es todo lo que desea, sugeriría usarlo en lugar de hacer algo más manual.
phils
... y si la traducción clave es realmente lo que quieres aquí, creo que esa es una pregunta diferente , y que debes hacerla por separado; y luego reformule su ejemplo para que esta pregunta sea más apropiada para el problema más general de "¿cómo simulo un evento clave en elisp?"
phils

Respuestas:

24

Puede alimentar eventos arbitrarios (pulsaciones de teclas, clics del mouse, etc.) al bucle de comandos al colocarlos en unread-command-events. Por ejemplo, lo siguiente hará que el ciclo del comando ejecute un corte la próxima vez que se ejecute:

(setq unread-command-events (listify-key-sequence "\C-g"))

Tenga en cuenta que esto solo alimenta eventos al bucle de comando, por lo que no hará nada interesante si está bucleando su propio código.

Un enfoque diferente, del que parece estar al tanto, es encontrar la función a la que está vinculada una tecla determinada y ejecutarla usted mismo:

(funcall (global-key-binding "\C-g"))

Esto ejecutará el comando de inmediato. Sin embargo, tenga en cuenta que algunos comandos tienen un comportamiento diferente dependiendo de si se los llama de forma interactiva, como los argumentos predeterminados. Querrás compensar eso usando call-interactively:

(call-interactively (global-key-binding "\C-g"))
jch
fuente
Leí sobre unread-command-eventspero no he podido descubrir cómo usarlo. Establecerlo no ha tenido ningún efecto para mí. ¿Hay algún buen ejemplo de cómo se usa?
nispio
Lo he visto cuando le pido al usuario que presione la barra espaciadora para continuar; si el usuario presiona algo más, continúa unread-command-events.
jch
@nispio: unread-command-eventses justo lo que dice su nombre. Puede examinar un evento y luego, según lo que sea, empujarlo condicionalmente u-c-epara que luego se procese normalmente. Hay muchos ejemplos de su uso en el código fuente de Emacs: grepes tu amigo.
Dibujó el
1
Pude ir unread-command-eventsa trabajar. La pieza que me faltaba antes era la listify-key-sequencefunción. Acababa de usar el vector de clave sin formato.
nispio
1
Gracias por esta respuesta Quería implementar pruebas no interactivas de mi sistema de finalización, así que utilicé esta idea para implementar una with-simulated-inputmacro que evalúa cualquier expresión con unread-command-eventslet-bound a una secuencia de teclas específica: github.com/DarwinAwardWinner/ido-ubiquitous/blob/…
Ryan C. Thompson
8

La forma más simple que conozco es simplemente usar execute-kbd-macro:

(defun foo () (interactive) (execute-kbd-macro (kbd "<escape>")))
(global-set-key (kbd "C-`") 'foo)
shosti
fuente
Evaluar lo anterior y luego presionar C-` me da un error apply: Wrong number of arguments: #[(ad--addoit-function ....
nispio
1
@nispio No es para mí. Ese error parece un consejo.
Malabarba
@Malabarba Creo que tienes razón. Después de comenzar de nuevo con emacs -Qese error no está presente. Sin embargo, sigo recibiendo este error:After 0 kbd macro iterations: foo: Lisp nesting exceeds `max-lisp-eval-depth'
nispio
Esto es realmente lo que estaba buscando. Por alguna extraña razón (probablemente algunos detalles de interacción con evil), llamar directamente a la función deseada tuvo un efecto inesperado en mi caso ( evilmi-jump-items), y tuve que usar(execute-kbd-macro (kbd "%"))
xji
4

Tomado de esta respuesta , puede usar global-set-key como este

(global-set-key (kbd "C-`") (kbd "<escape>"))

Que tratará C-`comoescape

Sin embargo, esto parece tener algunos problemas si la segunda combinación no ejecuta una función. Entonces, si escapese usa como Meta, entonces no funciona correctamente. Pero parece funcionar para comandos vinculados a funciones.

resueman
fuente
@nispio: En realidad, funciona, ya que el segundo argumento se convierte implícitamente en una macro de teclado.
shosti
1
La evaluación de la @shosti arriba y pulsando a continuación C-` me da un error: After 0 kbd macro iterations: command-execute: Lisp nesting exceeds `max-lisp-eval-depth'.
nispio
@nispio: Probablemente ya tenga C-`obligado ESCpor algún otro método, por lo que va a un bucle infinito.
shosti
@shosti Tenías razón. Demasiados eval-sexppasando en una sesión. :-) Pero intentar de nuevo con emacs -Qcausas C-` simplemente no hacer nada.
nispio
Dependiendo de su sistema, (kbd "<escape>")y (kbd "ESC")puede significar cosas diferentes, ¿ha probado ambos?
shosti
2

Después de leer la sugerencia de uso de jchunread-command-events , pude hackear una solución que hará algunas de las cosas que estoy buscando.

(defun my-simulate-key-event (event &optional N)
  "Simulate an arbitrary keypress event.

This function sets the `unread-command-events' variable in order to simulate a
series of key events given by EVENT. Can also For negative N, simulate the
specified key EVENT directly.  For positive N, removes the last N elements from
the list of key events in `this-command-keys' and then appends EVENT.  For N nil,
treat as N=1."
  (let ((prefix (listify-key-sequence (this-command-keys)))
         (key (listify-key-sequence event))
         (n (prefix-numeric-value N)))
     (if (< n 0)
         (setq prefix key)
       (nbutlast prefix n)
       (nconc prefix key))
       (setq unread-command-events prefix)))

Todavía hay una serie de problemas para resolver. Es decir, no obtengo el resultado correcto si llamo a esta función dos veces seguidas dentro de una sola defun.


Nota al margen:

Después de consultar la sugerencia de uso de phils,key-translation-map pude encontrar lo local-function-key-mapque también es muy útil para lograr algunos de mis objetivos más amplios.

nispio
fuente