Come posso simulare un evento chiave arbitraria da Elisp?


26

È possibile simulare un evento chiave arbitrario da elisp? Sono a conoscenza di modi in cui posso trovare l'associazione per una determinata chiave e quindi chiamare quel comando in modo interattivo, ma cosa succede se quell'evento chiave non è associato a un comando?

Ad esempio , se volessi associarmi C-`a comportarsi come la ESCchiave in tutti i contesti ?


Sembra che key-bindingssia il tag sbagliato se non si sta tentando di alias un'associazione chiave. Inoltre, forse dovresti cambiare il tuo esempio in qualcos'altro in modo che non venga confuso.
b4hand,

@ b4hand Sono aperto a suggerimenti per tag migliori. Non ci sono key-eventstag. Dovrei farne uno?
nispio,

mi sembra ragionevole, ma gli eventi potrebbero essere migliori poiché questo potrebbe essere applicabile anche agli eventi del mouse.
b4hand,

2
Sono ancora confuso se si desidera simulare un evento chiave in elisp o se si desidera specificamente la possibilità di far agire una chiave come se fosse un'altra chiave? I simili key-translation-mapfacilitano quest'ultimo, quindi se è tutto ciò che vuoi, suggerirei di usarlo piuttosto che fare qualcosa di più manuale.
phils,

... e se la traduzione chiave è davvero ciò che vuoi qui, penso che sia una domanda diversa , e che dovresti chiederla separatamente; e poi riscrivi il tuo esempio affinché questa domanda sia più appropriata al problema più generale di "come posso simulare un evento chiave in elisp?"
phils,

Risposte:


24

È possibile alimentare eventi arbitrari (sequenze di tasti, clic del mouse, ecc.) Nel ciclo di comandi inserendoli unread-command-events. Ad esempio, ciò che segue farà sì che il ciclo di comandi esegua un'interruzione alla successiva esecuzione:

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

Nota che questo alimenta solo gli eventi al ciclo di comando, quindi non farà nulla di interessante se esegui il ciclo nel tuo codice.

Un approccio diverso, di cui sembri essere consapevole, è quello di trovare la funzione alla quale è associato un determinato tasto ed eseguirlo tu stesso:

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

Questo eseguirà immediatamente il comando. Attenzione, tuttavia, che alcuni comandi hanno comportamenti diversi a seconda che vengano chiamati in modo interattivo, ad esempio argomenti predefiniti. Ti consigliamo di compensarlo usando call-interactively:

(call-interactively (global-key-binding "\C-g"))

Ho letto unread-command-eventsma non sono stato in grado di capire come usarlo. L'impostazione non ha avuto alcun effetto per me. Esistono buoni esempi di come viene utilizzato?
nispio,

L'ho visto usato quando ho chiesto all'utente di premere spazio per continuare - se l'utente preme qualcos'altro, continua unread-command-events.
Jch

@nispio: unread-command-eventsè proprio quello che dice il suo nome. È possibile esaminare un evento e quindi, a seconda di ciò che è, rimandarlo condizionalmente in u-c-emodo da poterlo elaborare normalmente. Ci sono molti esempi del suo utilizzo nel codice sorgente di Emacs - grepè tuo amico.
Ha

1
Sono stato in grado di andare unread-command-eventsal lavoro. Il pezzo che mi mancava prima era la listify-key-sequencefunzione. Avevo appena usato il vettore chiave grezza.
nispio,

1
Grazie per questa risposta Volevo implementare test non interattivi del mio sistema di completamento, quindi ho usato questa idea per implementare una with-simulated-inputmacro che valuti qualsiasi espressione unread-command-eventslegata a una sequenza di tasti specifica: github.com/DarwinAwardWinner/ido-ubiquitous/blob/…
Ryan C. Thompson,

8

Il modo più semplice che conosco è solo quello di usare execute-kbd-macro:

(defun foo () (interactive) (execute-kbd-macro (kbd "<escape>")))
(global-set-key (kbd "C-`") 'foo)

Valutare quanto sopra e quindi premere C-` mi dà un errore apply: Wrong number of arguments: #[(ad--addoit-function ....
nispio,

1
@nispio Non per me. Quell'errore sembra un consiglio.
Malabarba,

@Malabarba Penso che tu abbia ragione. Dopo l'avvio con emacs -Qquesto errore non è presente. Ricevo ancora questo errore:After 0 kbd macro iterations: foo: Lisp nesting exceeds `max-lisp-eval-depth'
nispio,

Questo è in realtà quello che stavo cercando. Per qualche strana ragione (probabilmente alcuni dettagli di interazione con evil), chiamare direttamente la funzione desiderata ha avuto un effetto inaspettato nel mio caso ( evilmi-jump-items), e ho dovuto usare(execute-kbd-macro (kbd "%"))
xji

4

Tratto da questa risposta , puoi usare global-set-key in questo modo

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

Che tratterà C-`comeescape

Questo sembra avere qualche problema se la seconda combinazione non esegue una funzione. Quindi, se escapeviene utilizzato come Meta, quindi non funziona correttamente. Ma sembra funzionare per comandi associati a funzioni.


@nispio: In realtà, funziona, poiché il secondo argomento è implicitamente convertito in una macro di tastiera.
shosti,

1
@shosti Valutare quanto sopra e quindi premendo C-` mi dà un errore: After 0 kbd macro iterations: command-execute: Lisp nesting exceeds `max-lisp-eval-depth'.
nispio,

@nispio: Probabilmente hai già C-`legato a ESCqualche altro metodo, quindi sta andando in un ciclo infinito.
shosti,

@shosti Avevi ragione. Troppi eval-sexpsuccedono in una sessione. :-) Ma riprovare con emacs -Qcause C-` semplicemente non fare nulla.
nispio,

A seconda del tuo sistema (kbd "<escape>")e (kbd "ESC")potrebbe significare cose diverse: hai provato entrambi?
shosti,

2

Dopo aver letto il suggerimento di jch da usare unread-command-events, sono stato in grado di hackerare insieme una soluzione che farà alcune delle cose che sto cercando.

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

Ci sono ancora diversi nodi da risolvere. Vale a dire, non ottengo il risultato corretto se chiamo questa funzione due volte di seguito all'interno di una singola defun.


Nota a margine:

Dopo aver verificato il suggerimento di phils da usare, key-translation-mapsono stato in grado di scoprire local-function-key-mapquale è anche molto utile per raggiungere alcuni dei miei obiettivi più ampi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.