Чому я не можу прив’язати свою функцію до ключа або викликати її за допомогою Mx?


13

Я написав функцію і хочу викликати її через Mx і прив’язати її до ключа. Це моя функція:

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

Якщо я спробую викликати це M-x my-function, я отримую помилку: [no match]у міні-буфері.

Якщо я спробую прив'язати його до клавіші (або клацання миші):

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

Схоже, це працює, але коли я намагаюся викликати це C-c a, я отримую помилку

Аргумент неправильного типу: commandp, my-function

Чому я не можу використовувати свою функцію?


5
Я даю це добровільне запитання як загальну відповідь на часто задавані питання з цього приводу. Не соромтеся розширювати чи уточнювати, однак має сенс зробити запитання та відповіді відкритими і корисними для людей із подібними проблемами!
Тайлер

1
Дякую за це, Тайлер. Я позначив питання Q модератору, щоб перетворити це на питання спільноти.
Дрю

Мене цікавить одне, чи має заголовок просто цитувати повідомлення про помилку. Людям це може бути простіше знайти, і це може дати відповіді, не пов'язані з просто додаванням interactive- наприклад, іноді команда зникає з нової версії бібліотеки. Помилка може бути викликана в будь-якому контексті, коли Emacs очікує команди.
Дрю

Було б добре, якби зараз люди шукали вікі, щоб, скажімо, commandpспробувати знайти інші запитання, які можна закрити як копії цього. Будьте уважні до читання запитань, хоча деякі з них відрізняються. У деяких випадках відповідь (і контекст Q), можливо, варто повторити тут. В інших випадках питання не пов’язане між собою і його слід залишити таким, який є (не закритим).
Дрю

1
@Drew Я не знав, як найкраще "рекламувати" це в заголовку. Помилка командного вискакування з’являється лише з прив'язками клавіш, тому люди, які наїжджають на це з напрямку Mx, не побачать з'єднання. Не впевнений, що найкращий баланс між чітким, стислим заголовком та заголовком, який з’явиться для всіх відповідних пошукових запитів. Сміливо налаштовуйте, як вам подобається!
Тайлер

Відповіді:


22

Основним моментом є те, що між функцією та командою є різниця .

У Emacs lisp функції за замовчуванням не інтерактивні. Це означає, що ви не можете отримати доступ до них через M-xабо прив’язати їх до клавіші або клацання миші. Якщо ви хочете це зробити, вам потрібно чітко оголосити функцію interactive, яка є , що ви робите, додаючи (interactive)форму в якості першого рядка в тілі (слідуючи рядку документації). Інтерактивна функція називається командою. Це пояснено в посібнику: (info "(elisp) Using Interactive") (онлайн-версія) .

Повідомлення про помилку, яке ви бачите, Wrong type argument: commandp, my-functionозначає, що ви намагаєтесь викликати функцію інтерактивно, але ця функція не є командою .

Щоб пояснити фактичну помилку, лист pчасто використовується в lisp для позначення предиката або тесту. У цьому випадку Emacs проводить тестування, my-functionщоб перевірити, чи це команда, що використовує тест commandp. Це не так, що призводить до помилки. Подібні помилки виникають щоразу, коли ви використовуєте об'єкт неправильного типу: якщо Emacs очікує рядок і ви передасте символ, ви можете побачити посилання stringp, наприклад.

Щоб відповісти на запитання, вам потрібно додати (interactive)рядок до визначення:

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

Є цілі багато варіантів для interactiveформи, підтримуючи всі види способів передачі інформації в вашу функцію. Перевірте в посібнику всі деталі.

Клавіатури макросів є окремим випадком у цьому контексті. Макрос клавіатури - це послідовність вхідних подій, представлена ​​у вигляді рядка. Макроси клавіатури поводяться як команди, тому ви можете прив’язати їх до клавіш, не турбуючись про додавання interactiveдекларації. Наприклад, у наступному:

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

"λ"є макросом клавіатури, тому ми можемо C-c lбез проблем зв’язати його . Якщо ми намагаємось зробити те ж саме з функцією, ми повинні бути впевнені, щоб визначити функцію як 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 λ

одну пропозицію, яку я хотів би додати, дайте зрозуміти, що вам потрібно зробити Cx Ce, перш ніж робити M-x my-functionу своєму прикладі. Також як початківця emacs я дійсно не на 100% зрозуміла, що саме C-x C-eробить або коли вам потрібно це запустити, але виявляється, що uh ... коли ви запускаєте його, він аналізує буфер і перезаписується my-functionв пам'яті, тому що якщо я не запускати C-x C-e M-xвиконує функцію з останнього разу, коли я запускавсяC-x C-e
jrh

Здається, що C-x C-e оцінює буфер , здається, що результат оцінювання буфера, що містить a, defun- це реєстрація функції, хоча ця стаття, здається, змушує мене думати, що вона повинна просто запустити функцію, і все ж єдине, що показано в minibuffer - це my-function(маючи на увазі, що він повертає функцію?), ні This is a great function. Я, мабуть, щось тут пропускаю.
jrh

1
Дякуємо за ваші коментарі, @jrh. Це питання та відповідь стосуються певного аспекту elisp, як зробити функцію інтерактивною (тобто перетворити функцію в команду). Ви запитуєте про більш фундаментальні аспекти еліпсу, і виходите за рамки цього питання. Я рекомендую працювати через Emacs Lisp Intro. Ви, мабуть, заплутані в різниці між оцінкою визначення функції, яка (повторно) визначає функцію, але насправді її не викликає, і викликом функції, яка запускає код функції.
Тайлер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.