Wie kann ich ein beliebiges Schlüsselereignis von Elisp simulieren?

26

Ist es möglich, ein beliebiges Schlüsselereignis aus elisp heraus zu simulieren? Ich bin mir bewusst, wie ich die Bindung für einen bestimmten Schlüssel finden und diesen Befehl dann interaktiv aufrufen kann, aber was ist, wenn dieses Schlüsselereignis nicht an einen Befehl gebunden ist?

Als ein Beispiel , was passiert , wenn ich wollte , binden , C-`das gleiche wie die verhaltene ESCSchlüssel in allen Kontexten ?

Nispio
quelle
Es scheint, als wäre key-bindingses das falsche Tag, wenn Sie nicht versuchen, eine Schlüsselbindung als Alias ​​zu kennzeichnen. Vielleicht sollten Sie Ihr Beispiel auch in etwas anderes ändern, damit es nicht verwirrt wird.
b4hand
@b4hand Ich bin offen für Vorschläge für bessere Tags. Es gibt kein key-eventsTag. Soll ich eins machen?
Nispio
Klingt für mich vernünftig, aber Ereignisse sind möglicherweise besser, da dies auch für Mausereignisse gelten kann.
b4hand
2
Ich bin immer noch verwirrt, ob Sie ein Schlüsselereignis in elisp simulieren möchten oder ob Sie speziell die Möglichkeit haben möchten, einen Schlüssel so wirken zu lassen, als wäre es ein anderer Schlüssel? Das mag von key-translation-mapden letzteren erleichtern, so dass , wenn das ist alles , was Sie wollen, würde ich es empfehlen die Verwendung nicht mehr manuell etwas zu tun.
Phils
... und wenn die Schlüsselübersetzung wirklich das ist, was Sie hier wollen, denke ich, ist das eine andere Frage, und das sollten Sie separat stellen. und formulieren Sie dann Ihr Beispiel neu, damit diese Frage dem allgemeineren Problem "Wie simuliere ich ein Schlüsselereignis in elisp?"
Phils

Antworten:

24

Sie können beliebige Ereignisse (Tastenanschläge, Mausklicks usw.) in die Befehlsschleife einfügen, indem Sie sie dort platzieren unread-command-events. Das Folgende bewirkt beispielsweise, dass die Befehlsschleife beim nächsten Ausführen eine Unterbrechung ausführt:

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

Beachten Sie, dass dies nur Ereignisse an die Befehlsschleife weiterleitet. Wenn Sie also Ihren eigenen Code einschleifen, ist dies nicht von Interesse.

Ein anderer Ansatz, den Sie zu kennen scheinen, besteht darin, die Funktion, an die ein bestimmter Schlüssel gebunden ist, zu finden und selbst auszuführen:

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

Dadurch wird der Befehl sofort ausgeführt. Beachten Sie jedoch, dass einige Befehle ein unterschiedliches Verhalten aufweisen, je nachdem, ob sie interaktiv aufgerufen werden, z. B. Standardargumente. Sie möchten dies ausgleichen, indem Sie Folgendes verwenden call-interactively:

(call-interactively (global-key-binding "\C-g"))
jch
quelle
Ich habe darüber gelesen, unread-command-eventsaber ich konnte nicht herausfinden, wie man es benutzt. Das Einstellen hat für mich keine Wirkung. Gibt es ein gutes Beispiel für die Verwendung?
Nispio
Ich habe gesehen, wie es verwendet wurde, wenn der Benutzer aufgefordert wurde, die Leertaste zu drücken, um fortzufahren. Wenn der Benutzer etwas anderes drückt, wird es fortgesetzt unread-command-events.
23.
@nispio: unread-command-eventsist genau das, was der Name sagt. Sie können ein Ereignis untersuchen und es dann, je nachdem, was es ist, unter bestimmten Bedingungen zurückschieben, u-c-edamit es normal verarbeitet wird. Es gibt viele Beispiele für die Verwendung im Emacs-Quellcode - grepist Ihr Freund.
Drew
1
Ich konnte mich unread-command-eventsan die Arbeit machen. Das Stück, das ich vorher vermisst habe, war die listify-key-sequenceFunktion. Ich hatte gerade den rohen Schlüsselvektor verwendet.
Nispio
1
Danke für diese Antwort. Ich wollte nicht-interaktive Tests für mein Vervollständigungssystem implementieren. Daher habe ich diese Idee verwendet, um ein with-simulated-inputMakro zu implementieren , das alle Ausdrücke auswertet unread-command-events, die an eine bestimmte Schlüsselsequenz gebunden sind: github.com/DarwinAwardWinner/ido-ubiquitous/blob/…
Ryan C. Thompson
8

Der einfachste Weg, den ich kenne, ist einfach zu benutzen execute-kbd-macro:

(defun foo () (interactive) (execute-kbd-macro (kbd "<escape>")))
(global-set-key (kbd "C-`") 'foo)
Shosti
quelle
Wenn C-` ich das Obige auswerte und dann drücke, erhalte ich einen Fehler apply: Wrong number of arguments: #[(ad--addoit-function ....
Nispio
1
@nispio Nicht für mich. Dieser Fehler sieht nach einem Rat aus.
Malabarba
@ Malabarba Ich denke du hast recht. Nach dem Neustart mit emacs -Qdiesem Fehler ist nicht vorhanden. Ich bekomme immer noch diese Fehlermeldung:After 0 kbd macro iterations: foo: Lisp nesting exceeds `max-lisp-eval-depth'
Nispio
Das ist genau das, wonach ich gesucht habe. Aus irgendeinem seltsamen Grund (wahrscheinlich einige Interaktionsdetails mit evil) hatte der direkte Aufruf der gewünschten Funktion in meinem Fall ( evilmi-jump-items) einen unerwarteten Effekt , und ich musste(execute-kbd-macro (kbd "%"))
xji
4

Aus dieser Antwort können Sie den folgenden Global-Set-Schlüssel verwenden

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

Welches wird C-`als behandelnescape

Dies scheint jedoch einige Probleme zu haben, wenn die zweite Kombination keine Funktion ausführt. Wenn escapealso wie verwendet wird Meta, dann funktioniert es nicht richtig. Aber es scheint für Befehle zu funktionieren, die an Funktionen gebunden sind.

Retter
quelle
@nispio: Eigentlich funktioniert es, da das zweite Argument implizit in ein Tastaturmakro konvertiert wird.
Shosti
1
@shosti die oben auswerten und dann die Taste C-` gibt mir eine Fehlermeldung: After 0 kbd macro iterations: command-execute: Lisp nesting exceeds `max-lisp-eval-depth'.
Nispio
@nispio: Sie haben sich wahrscheinlich schon C-an ESCeine andere Methode gebunden , es geht also in eine Endlosschleife.
Shosti
@shosti Du hattest recht. eval-sexpIn einer Sitzung laufen zu viele . :-) Aber nochmal versuchen mit emacs -QAnlass C-` einfach nichts zu machen.
Nispio
Abhängig von Ihrem System (kbd "<escape>")und (kbd "ESC")möglicherweise mit unterschiedlichen Bedeutungen - haben Sie beide ausprobiert?
Shosti
2

Nachdem ich den Vorschlag von jch zur Verwendung gelesen hatte , unread-command-eventswar ich in der Lage, eine Lösung zu hacken, die einige der Dinge erledigt, nach denen ich suche.

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

Es gibt noch eine Reihe von Knicken zu lösen. Ich erhalte nämlich nicht das richtige Ergebnis, wenn ich diese Funktion zweimal hintereinander innerhalb einer einzigen aufrufe defun.


Randnotiz:

Nachdem ich den Vorschlag von phils zur Verwendung durchgesehen hatte,key-translation-map konnte ich feststellen, dass local-function-key-mapdies auch sehr hilfreich ist, um einige meiner übergeordneten Ziele zu erreichen.

Nispio
quelle