Mengapa saya tidak bisa mengikat fungsi saya ke kunci atau menyebutnya dengan Mx?

13

Saya menulis sebuah fungsi, dan saya ingin menyebutnya melalui Mx, dan mengikatnya ke sebuah tombol. Ini adalah fungsi saya:

(defun my-function ()
    (message "This is a great function"))

Jika saya mencoba memanggilnya M-x my-function, saya mendapatkan kesalahan: [no match]di mini-buffer.

Jika saya mencoba mengikatnya ke tombol (atau klik mouse):

(global-set-key (kbd "C-c a") 'my-function)

Tampaknya berfungsi, tetapi ketika saya mencoba memanggilnya C-c a, saya mendapatkan kesalahan

Argumen tipe yang salah: commandp, my-function

Mengapa saya tidak bisa menggunakan fungsi saya?

Tyler
sumber
5
Saya mengajukan pertanyaan ini sebagai tanggapan umum terhadap pertanyaan yang sering diajukan tentang hal ini. Merasa bebas untuk memperluas atau mengklarifikasi namun masuk akal untuk membuat pertanyaan dan jawaban dapat ditemukan oleh & berguna bagi orang-orang dengan masalah yang sama!
Tyler
1
Terima kasih sudah melakukan ini, Tyler. Saya menandai Q untuk moderator untuk mengonversi ini menjadi pertanyaan komunitas.
Drew
Satu hal yang saya bertanya-tanya tentang apakah judul seharusnya mengutip pesan kesalahan. Itu mungkin lebih mudah bagi orang untuk menemukan, dan mungkin memungkinkan untuk jawaban yang tidak harus dilakukan hanya dengan menambahkan interactive- misalnya, kadang-kadang sebuah perintah menghilang dari versi baru perpustakaan. Kesalahan dapat dimunculkan dalam konteks apa pun di mana Emacs mengharapkan perintah.
Drew
Akan lebih baik jika orang-orang sekarang mencari wiki untuk, katakanlah commandp, untuk mencoba menemukan Qs lain yang dapat ditutup sebagai duplikat dari wiki ini. Berhati-hatilah untuk membaca T&J, karena ada beberapa yang berbeda. Dalam beberapa kasus, jawabannya (dan konteks Q) mungkin perlu diulang di sini. Dalam kasus lain , pertanyaannya tidak terkait dan harus dibiarkan apa adanya (tidak tertutup).
Drew
1
@Drew Aku tidak yakin bagaimana cara terbaik untuk 'mengiklankan' ini dalam judul. Kesalahan commandp hanya muncul dengan keybindings, sehingga orang-orang berlari ke ini dari arah Mx tidak akan melihat koneksi. Tidak yakin apa keseimbangan terbaik antara judul yang jelas, ringkas dan judul yang akan muncul untuk semua permintaan pencarian yang relevan. Jangan ragu untuk mengubah yang Anda inginkan!
Tyler

Jawaban:

22

Titik intinya adalah bahwa ada perbedaan antara fungsi dan perintah .

Dalam Emacs lisp, fungsi tidak dapat dipanggil secara interaktif secara default. Itu berarti Anda tidak dapat mengaksesnya melalui M-xatau mengikat mereka ke tombol atau klik mouse. Jika Anda ingin melakukan itu, Anda harus secara eksplisit mendeklarasikan fungsi tersebut interactive, yang Anda lakukan dengan menambahkan (interactive)formulir sebagai baris pertama dalam tubuh (mengikuti string dokumentasi). Fungsi interaktif disebut perintah. Ini dijelaskan dalam manual: (info "(elisp) Using Interactive") (versi online) .

Pesan kesalahan yang Anda lihat,, Wrong type argument: commandp, my-functionmenunjukkan bahwa Anda mencoba memanggil suatu fungsi secara interaktif, tetapi fungsi itu bukan perintah .

Untuk menjelaskan kesalahan yang sebenarnya, surat pitu sering digunakan dalam lisp untuk menunjukkan predikat atau tes. Dalam kasus ini, Emacs sedang menguji my-functionuntuk melihat apakah itu adalah perintah menggunakan tes commandp. Bukan, yang mengarah ke kesalahan. Kesalahan serupa muncul setiap kali Anda menggunakan objek dengan tipe yang salah: jika Emacs mengharapkan string dan Anda melewati simbol, Anda mungkin melihat referensi stringp, misalnya.

Untuk menjawab pertanyaan saat ditanyakan, Anda perlu menambahkan (interactive)baris ke definisi:

(defun my-function ()
    (interactive)
    (message "This is a great function"))

Ada banyak opsi untuk interactiveformulir, mendukung semua jenis cara menyampaikan informasi ke fungsi Anda. Periksa manual untuk semua detail.

Makro keyboard adalah kasus khusus dalam konteks ini. Makro keyboard adalah urutan acara input, direpresentasikan sebagai string. Makro keyboard berperilaku seperti perintah, sehingga Anda dapat mengikatnya ke tombol tanpa khawatir menambahkan interactivedeklarasi. Misalnya, berikut ini:

(global-set-key (kbd "C-c l") "λ")

"λ"adalah makro keyboard, jadi kita dapat mengikatnya C-c ltanpa masalah. Jika kita mencoba melakukan hal yang sama dengan suatu fungsi, kita harus memastikan untuk mendefinisikan fungsi sebagai interactive:

(global-set-key (kbd "C-c k") 
  (lambda () (insert "λ"))
;; C-c k is undefined! We tried to bind it to a function

(global-set-key (kbd "C-c m") 
  (lambda () (interactive) (insert "λ"))
;; C-c m is bound to a command that inserts λ
Tyler
sumber
satu saran yang ingin saya tambahkan, jelaskan bahwa Anda perlu melakukan Cx Ce sebelum melakukan M-x my-functiondalam contoh Anda. Juga sebagai seorang pemula emacs, saya benar-benar belum 100% jelas tentang apa tepatnya C-x C-eatau kapan Anda perlu menjalankannya, tetapi tampaknya uh ... ketika Anda menjalankannya, ia mem-parsing buffer dan menimpa my-functiondalam memori karena jika saya jangan lari C-x C-e M-xmenjalankan fungsi dari terakhir kali saya berlariC-x C-e
jrh
Akan muncul yang C-x C-e mengevaluasi buffer , tampaknya hasil mengevaluasi buffer yang mengandung defunmendaftarkan fungsi, meskipun artikel ini tampaknya membuat saya berpikir bahwa itu hanya menjalankan fungsi, namun satu-satunya hal yang ditunjukkan dalam minibuffer adalah my-function(menyiratkan itu mengembalikan fungsi?), bukan This is a great function. Saya pasti kehilangan sesuatu di sini.
jrh
1
Terima kasih atas komentar Anda, @jrh. Pertanyaan & jawaban ini membahas aspek tertentu dari elisp, bagaimana membuat fungsi menjadi interaktif (yaitu, mengubah fungsi menjadi perintah). Anda bertanya tentang aspek yang lebih mendasar dari elisp, dan berada di luar cakupan pertanyaan ini. Saya sarankan bekerja melalui Intro Emacs Lisp. Anda tampaknya bingung tentang perbedaan antara mengevaluasi definisi fungsi, yang (kembali) mendefinisikan fungsi tetapi sebenarnya tidak memanggilnya, dan memanggil fungsi, yang menjalankan kode fungsi.
Tyler