Bagaimana saya bisa mensimulasikan peristiwa kunci arbiter dari Elisp?

26

Apakah mungkin untuk mensimulasikan peristiwa kunci sewenang-wenang dari elisp? Saya menyadari cara-cara yang saya dapat menemukan ikatan untuk kunci yang diberikan, dan kemudian memanggil perintah itu secara interaktif, tetapi bagaimana jika peristiwa kunci itu tidak terikat pada suatu perintah?

Sebagai satu contoh , bagaimana jika saya ingin mengikat C-`untuk berperilaku sama dengan ESCkunci dalam semua konteks ?

nispio
sumber
Sepertinya key-bindingsini adalah tag yang salah jika Anda tidak mencoba alias mengikat kunci. Juga, mungkin Anda harus mengubah contoh Anda ke sesuatu yang lain sehingga tidak menjadi bingung.
b4hand
@ b4hand Saya terbuka untuk saran untuk tag yang lebih baik. Tidak ada key-eventstag. Haruskah saya membuatnya?
nispio
kedengarannya masuk akal bagi saya, tetapi acara mungkin lebih baik karena ini juga bisa berlaku untuk acara mouse.
b4hand
2
Saya masih bingung apakah Anda ingin mensimulasikan peristiwa kunci di elisp, atau Anda secara khusus ingin kemampuan untuk membuat tindakan kunci seolah-olah itu adalah kunci lain? Suka key-translation-mapmemfasilitasi yang terakhir, jadi jika itu yang Anda inginkan, saya sarankan menggunakannya daripada melakukan sesuatu yang lebih manual.
phils
... dan jika terjemahan utama adalah yang Anda inginkan di sini, saya pikir itu pertanyaan yang berbeda , dan Anda harus menanyakannya secara terpisah; dan kemudian tulis kembali contoh Anda untuk pertanyaan ini agar lebih sesuai dengan masalah yang lebih umum dari "bagaimana saya mensimulasikan peristiwa kunci di elisp?"
phils

Jawaban:

24

Anda bisa memberi makan acara yang sewenang-wenang (penekanan tombol, klik mouse, dll.) Ke loop perintah dengan meletakkannya di atasnya unread-command-events. Misalnya, berikut ini akan menyebabkan loop perintah untuk menjalankan istirahat saat dijalankan berikutnya:

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

Perhatikan bahwa ini hanya mengumpankan peristiwa ke loop perintah, jadi tidak ada yang menarik jika Anda mengulangi di kode Anda sendiri.

Pendekatan lain, yang tampaknya Anda sadari, adalah menemukan fungsi yang terikat dengan kunci tertentu, dan menjalankannya sendiri:

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

Ini akan segera menjalankan perintah. Berhati-hatilah, bagaimanapun, bahwa beberapa perintah memiliki perilaku yang berbeda tergantung pada apakah mereka dipanggil secara interaktif, seperti argumen default. Anda ingin mengompensasi itu dengan menggunakan call-interactively:

(call-interactively (global-key-binding "\C-g"))
jch
sumber
Saya membaca tentang unread-command-eventstetapi saya belum tahu bagaimana menggunakannya. Pengaturan itu tidak berpengaruh bagi saya. Apakah ada contoh yang baik tentang bagaimana ini digunakan?
nispio
Saya pernah melihatnya digunakan ketika meminta pengguna untuk menekan spasi untuk melanjutkan - jika pengguna menekan hal lain, ia melanjutkan unread-command-events.
jch
@nispio: unread-command-eventstepat seperti namanya. Anda dapat memeriksa suatu peristiwa dan kemudian, tergantung pada apa itu, mendorongnya kembali secara kondisional u-c-esehingga kemudian akan diproses secara normal. Ada banyak contoh penggunaannya dalam kode sumber Emacs - grepadalah teman Anda.
Drew
1
Saya bisa mulai unread-command-eventsbekerja. Bagian yang hilang sebelumnya adalah listify-key-sequencefungsinya. Saya baru saja menggunakan vektor kunci mentah.
nispio
1
Terima kasih atas jawaban ini. Saya ingin menerapkan tes non-interaktif dari sistem penyelesaian saya, jadi saya menggunakan ide ini untuk mengimplementasikan with-simulated-inputmakro yang mengevaluasi ekspresi apa pun dengan unread-command-eventsmembiarkan-terikat ke urutan kunci yang ditentukan: github.com/DarwinAwardWinner/ido-ubiquitous/blob/…
Ryan C. Thompson
8

Cara paling sederhana yang saya tahu adalah menggunakan execute-kbd-macro:

(defun foo () (interactive) (execute-kbd-macro (kbd "<escape>")))
(global-set-key (kbd "C-`") 'foo)
shosti
sumber
Mengevaluasi hal di atas dan kemudian menekan C-` memberi saya kesalahan apply: Wrong number of arguments: #[(ad--addoit-function ....
nispio
1
@nispio Bukan untuk saya. Kesalahan itu terlihat seperti saran.
Malabarba
@Malabarba, saya pikir Anda benar. Setelah memulai dengan emacs -Qkesalahan itu tidak ada. Saya masih mendapatkan kesalahan ini:After 0 kbd macro iterations: foo: Lisp nesting exceeds `max-lisp-eval-depth'
nispio
Ini sebenarnya yang saya cari. Untuk beberapa alasan aneh (mungkin beberapa detail interaksi dengan evil), langsung memanggil fungsi yang diinginkan memiliki efek yang tidak terduga dalam kasus saya ( evilmi-jump-items), dan saya harus menggunakan(execute-kbd-macro (kbd "%"))
xji
4

Diambil dari jawaban ini , Anda dapat menggunakan global-set-key seperti ini

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

Yang akan diperlakukan C-`sebagaiescape

Ini tampaknya memiliki beberapa masalah meskipun jika kombinasi kedua tidak menjalankan suatu fungsi. Jadi jika escapedigunakan seperti Meta, maka itu tidak berfungsi dengan benar. Tetapi tampaknya berfungsi untuk perintah yang terikat pada fungsi.

resueman
sumber
@nispio: Sebenarnya, ini berfungsi, karena argumen kedua secara implisit dikonversi ke makro keyboard.
shosti
1
@shosti Mengevaluasi atas dan kemudian menekan C-` memberikan saya sebuah kesalahan: After 0 kbd macro iterations: command-execute: Lisp nesting exceeds `max-lisp-eval-depth'.
nispio
@nispio: Anda mungkin sudah C-terikat ESColeh beberapa metode lain, jadi ini akan menjadi infinite loop.
shosti
@shosti Anda benar. Terlalu banyak yang eval-sexpterjadi dalam satu sesi. :-) Tetapi mencoba lagi dengan emacs -Qalasan C-` untuk tidak melakukan apa-apa.
nispio
Bergantung pada sistem Anda, (kbd "<escape>")dan (kbd "ESC")mungkin memiliki arti yang berbeda - sudahkah Anda mencoba keduanya?
shosti
2

Setelah membaca saran dari jch untuk digunakan unread-command-events, saya dapat meretas bersama solusi yang akan melakukan beberapa hal yang saya cari.

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

Masih ada sejumlah kekusutan yang harus diselesaikan. Yaitu, saya tidak mendapatkan hasil yang benar jika saya memanggil fungsi ini dua kali berturut-turut dalam satu defun.


Catatan samping:

Setelah memeriksa saran phils untuk menggunakan key-translation-mapsaya dapat menemukan local-function-key-mapyang juga sangat membantu dalam mencapai beberapa tujuan saya yang lebih luas.

nispio
sumber