Почему я не могу привязать свою функцию к клавише или вызвать ее с помощью 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, которые можно закрыть как дубликаты этого. Будьте внимательны при чтении вопросов и ответов, так как некоторые отличаются. В некоторых случаях ответ (и контекст Q), возможно, стоит повторить здесь. В других случаях вопрос не связан и должен быть оставлен как есть (не закрыт).
Дрю
1
@Drew Я не был уверен, как лучше «рекламировать» это в названии. Ошибка commandp всплывает только с сочетаниями клавиш, поэтому люди, сталкивающиеся с этим со стороны 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делает или когда вам нужно его запустить, но кажется, что ... когда вы запускаете его, он анализирует буфер и перезаписывает my-functionв памяти, потому что если я не запускать - C-x C-e M-xзапускает функцию с момента последнего C-x C-e
запуска
Может показаться, что C-x C-e оценивает буфер , кажется, что результатом оценки буфера, содержащего a, defunявляется регистрация функции, хотя эта статья, кажется, заставляет меня думать, что она должна просто запустить функцию, и все же единственное, что показано в минибуфере, это my-function(подразумевая, что возвращает функцию?), а не This is a great function. Я должен что-то здесь упустить.
18:18
1
Спасибо за ваши комментарии, @jrh. Этот вопрос и ответ касаются определенного аспекта elisp, как сделать функцию интерактивной (т.е. превратить функцию в команду). Вы спрашиваете о более фундаментальных аспектах elisp и выходите за рамки этого вопроса. Я рекомендую работать через введение в Emacs Lisp. Вы, кажется, смущены разницей между оценкой определения функции, которая (повторно) определяет функцию, но на самом деле не вызывает ее, и вызовом функции, которая выполняет код функции.
Тайлер