Jak mogę zasymulować arbitralne kluczowe wydarzenie z Elisp?

26

Czy można symulować dowolne zdarzenie klucza z elisp? Wiem, w jaki sposób mogę znaleźć powiązanie dla danego klucza, a następnie wywołać to polecenie interaktywnie, ale co, jeśli to zdarzenie klucza nie jest powiązane z poleceniem?

Jako przykład , co jeśli chciałbym powiązać, C-`aby zachowywał się tak samo jak ESCklucz we wszystkich kontekstach ?

nispio
źródło
Wygląda na key-bindingsto, że jest to zły tag, jeśli nie próbujesz aliasu powiązania klucza. Być może powinieneś zmienić przykład na coś innego, aby się nie pomylić.
b4hand
@ b4hand Jestem otwarty na propozycje lepszych tagów. Nie ma key-eventstagu. Powinienem zrobić?
nispio
brzmi dla mnie rozsądnie, ale zdarzenia mogą być lepsze, ponieważ mogą dotyczyć również zdarzeń myszy.
b4hand
2
Nadal jestem zdezorientowany, czy chcesz symulować kluczowe wydarzenie w elisp, czy konkretnie chcesz mieć możliwość działania tak, jakby to był inny klucz? Takie key-translation-mapułatwienia ułatwiają to drugie, więc jeśli to wszystko, czego chcesz, sugerowałbym użycie go zamiast robienia czegokolwiek bardziej ręcznego.
phils
... a jeśli naprawdę kluczowe jest tłumaczenie kluczowe, myślę, że to inne pytanie i powinieneś zadać to osobno; a następnie powtórz swój przykład, aby to pytanie było bardziej odpowiednie dla bardziej ogólnego problemu „jak symulować kluczowe wydarzenie w elisp?”
phils

Odpowiedzi:

24

Możesz podać dowolne zdarzenia (naciśnięcia klawiszy, kliknięcia myszą itp.) Do pętli poleceń, umieszczając je na unread-command-events. Na przykład następujące polecenie spowoduje, że pętla poleceń wykona przerwanie przy następnym uruchomieniu:

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

Zauważ, że to tylko przekazuje zdarzenia do pętli poleceń, więc nie zrobi nic interesującego, jeśli zapętlasz swój własny kod.

Innym podejściem, o którym zdajesz się być świadomy, jest znalezienie funkcji, do której przypisany jest dany klucz, i wykonanie go samodzielnie:

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

Spowoduje to natychmiastowe wykonanie polecenia. Uważaj jednak na to, że niektóre polecenia zachowują się inaczej w zależności od tego, czy są wywoływane interaktywnie, na przykład domyślne argumenty. Będziesz chciał to zrekompensować, używając call-interactively:

(call-interactively (global-key-binding "\C-g"))
jch
źródło
Czytałem o, unread-command-eventsale nie byłem w stanie dowiedzieć się, jak go używać. Ustawienie tego nie miało dla mnie żadnego efektu. Czy istnieje dobry przykład tego, w jaki sposób jest wykorzystywany?
nispio
Widziałem to używane, gdy prosił użytkownika o naciśnięcie spacji, aby kontynuować - jeśli użytkownik naciska coś innego, włącza się unread-command-events.
jch
@nispio: unread-command-eventstak właśnie mówi jego nazwa. Możesz zbadać zdarzenie, a następnie, w zależności od tego, co jest, warunkowo wepchnij je z powrotem u-c-e, aby następnie zostało przetworzone normalnie. Istnieje wiele przykładów jego zastosowania w kodzie źródłowym Emacsa - grepjest twoim przyjacielem.
Drew
1
Byłem w stanie zabrać się unread-command-eventsdo pracy. Elementem, którego wcześniej brakowało, była listify-key-sequencefunkcja. Właśnie korzystałem z surowego wektora klucza.
nispio
1
Dziękuję za tę odpowiedź. Chciałem zaimplementować nieinteraktywne testy mojego systemu uzupełniania, więc skorzystałem z tego pomysłu, aby zaimplementować with-simulated-inputmakro, które ocenia dowolne wyrażenie z unread-command-eventslet-bound do określonej sekwencji klawiszy: github.com/DarwinAwardWinner/ido-ubiquitous/blob/…
Ryan C. Thompson
8

Najprostszy sposób, jaki znam, to po prostu użyć execute-kbd-macro:

(defun foo () (interactive) (execute-kbd-macro (kbd "<escape>")))
(global-set-key (kbd "C-`") 'foo)
shosti
źródło
Ocena powyższego, a następnie naciśnięcie C-` powoduje błąd apply: Wrong number of arguments: #[(ad--addoit-function ....
nispio
1
@nispio Nie dla mnie. Ten błąd wygląda jak rada.
Malabarba
@Malabarba Myślę, że masz rację. Po uruchomieniu od nowa z emacs -Qtym błędem nie występuje. Nadal After 0 kbd macro iterations: foo: Lisp nesting exceeds `max-lisp-eval-depth'
pojawia
Właśnie tego szukałem. Z jakiegoś dziwnego powodu (prawdopodobnie niektóre szczegóły interakcji evil) bezpośrednie wywołanie żądanej funkcji miało nieoczekiwany efekt w moim przypadku ( evilmi-jump-items) i musiałem użyć(execute-kbd-macro (kbd "%"))
xji 1'15
4

Na podstawie tej odpowiedzi można użyć globalnego zestawu kluczy w ten sposób

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

Co będzie traktowane C-`jakoescape

To wydaje się mieć pewne problemy, jeśli druga kombinacja nie wykonuje funkcji. Więc jeśli escapejest używany tak Meta, to nie działa poprawnie. Ale wydaje się, że działa w przypadku poleceń powiązanych z funkcjami.

ratownik
źródło
@nispio: Właściwie to działa, ponieważ drugi argument jest domyślnie konwertowany na makro klawiatury.
shosti
1
@shosti Oceniając powyższe, a następnie naciśnięcie C-` daje mi błąd: After 0 kbd macro iterations: command-execute: Lisp nesting exceeds `max-lisp-eval-depth'.
nispio
@nispio: Prawdopodobnie już C-` związałeś się ESCz jakąś inną metodą, więc przechodzi ona w nieskończoną pętlę.
shosti
@shosti Miałeś rację. Zbyt wielu eval-sexpdzieje się w jednej sesji. :-) Ale spróbuj ponownie z emacs -Qprzyczynami, C-` aby po prostu nic nie robić.
nispio
W zależności od systemu, (kbd "<escape>")a (kbd "ESC")może oznaczać różne rzeczy - czy próbowałeś obu?
shosti
2

Po przeczytaniu sugestii z jch do użycia unread-command-events, byłem w stanie zhakować razem rozwiązanie, które zrobi niektóre z rzeczy, których szukam.

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

Nadal jest wiele spraw do załatwienia. Mianowicie nie otrzymuję poprawnego wyniku, jeśli wywołam tę funkcję dwa razy z rzędu w ciągu jednego defun.


Dygresja:

Po zapoznaniu się z sugestią Filipa dotyczącą zastosowania key-translation-map, mogłem znaleźć, local-function-key-mapktóra jest również bardzo pomocna w osiągnięciu niektórych z moich szerszych celów.

nispio
źródło