Почему я не могу привязать свою функцию к клавише или вызвать ее с помощью 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. Вы, кажется, смущены разницей между оценкой определения функции, которая (повторно) определяет функцию, но на самом деле не вызывает ее, и вызовом функции, которая выполняет код функции.
— Тайлер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.