¿Por qué no puedo vincular mi función a una tecla o llamarla con Mx?

13

Escribí una función, y quiero llamarla a través de Mx, y vincularla a una tecla. Esta es mi función:

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

Si trato de llamarlo M-x my-function, aparece el error: [no match]en el mini-buffer.

Si trato de vincularlo a una tecla (o clic del mouse):

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

Parece funcionar, pero cuando trato de llamarlo C-c a, aparece el error

Argumento de tipo incorrecto: commandp, my-function

¿Por qué no puedo usar mi función?

Tyler
fuente
55
Soy voluntario en esta pregunta como respuesta genérica a las preguntas frecuentes sobre este tema. ¡Siéntase libre de ampliar o aclarar, sin embargo, tiene sentido hacer que la pregunta y la respuesta sean reconocibles y útiles para personas con problemas similares!
Tyler
1
Gracias por hacer esto, Tyler. Marqué la Q de un moderador para convertir esto en una pregunta de la comunidad.
Dibujó el
Una cosa que me pregunto es si el título solo debe citar el mensaje de error. Eso podría ser más fácil de encontrar para las personas, y podría permitir respuestas que no tienen que ver con solo agregar interactive, por ejemplo, a veces un comando desaparece de una nueva versión de una biblioteca. El error se puede generar en cualquier contexto donde Emacs espera un comando.
Dibujó el
Sería bueno si la gente buscara en la wiki, por ejemplo commandp, para tratar de encontrar otras Q que puedan cerrarse como duplicados de esta. Sin embargo, tenga cuidado de leer las preguntas y respuestas, ya que algunas son diferentes. En algunos casos, la respuesta (y el contexto Q) puede valer la pena repetir aquí. En otros casos, la pregunta no está relacionada y debe dejarse como está (no cerrada).
Dibujó el
1
@Drew No estaba seguro de la mejor manera de "anunciar" esto en el título. El error commandp solo aparece con combinaciones de teclas, por lo que las personas que se encuentren con esto desde la dirección Mx no verán la conexión. No estoy seguro de cuál es el mejor equilibrio entre un título claro y conciso y un título que aparecerá para todas las consultas de búsqueda relevantes. ¡Siéntete libre de ajustar como quieras!
Tyler

Respuestas:

22

El punto central es que hay una diferencia entre una función y un comando .

En Emacs lisp, las funciones no son interactivamente invocables por defecto. Eso significa que no puede acceder a ellos M-xni vincularlos a una tecla o hacer clic con el mouse. Si desea hacer eso, debe declarar explícitamente la función interactive, lo que hace agregando un (interactive)formulario como la primera línea en el cuerpo (siguiendo la cadena de documentación). Una función interactiva se denomina comando. Esto se explica en el manual: (info "(elisp) Using Interactive") (versión en línea) .

El mensaje de error que ves Wrong type argument: commandp, my-functionindica que estás intentando llamar a una función de manera interactiva, pero esa función no es un comando .

Para explicar el error real, la letra a pmenudo se usa en ceceo para indicar un predicado o prueba. En este caso, Emacs está probando my-functionpara ver si es un comando que usa la prueba commandp. No lo es, lo que lleva al error. Aparecen errores similares cada vez que utiliza un objeto del tipo incorrecto: si Emacs espera una cadena y le pasa un símbolo, puede ver una referencia stringp, por ejemplo.

Para responder la pregunta tal como se le preguntó, debe agregar la (interactive)línea a la definición:

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

Hay muchas opciones para el interactiveformulario, que admiten todo tipo de formas de pasar información a su función. Consulte el manual para todos los detalles.

Las macros de teclado son un caso especial en este contexto. Una macro de teclado es una secuencia de eventos de entrada, representada como una cadena. Las macros de teclado se comportan como comandos, por lo que puede vincularlas a teclas sin preocuparse de agregar una interactivedeclaración. Por ejemplo, en lo siguiente:

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

"λ"es una macro de teclado, por lo que podemos vincularla C-c lsin ningún problema. Si intentamos hacer lo mismo con una función, debemos asegurarnos de definir la función como 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
fuente
Una sugerencia que me gustaría agregar, aclare que necesita hacer Cx Ce antes de hacerlo M-x my-functionen su ejemplo. Además, como principiante de emacs, todavía no estoy 100% claro sobre qué C-x C-ehace exactamente o cuándo necesitas ejecutarlo, pero parece que eh ... cuando lo ejecutas, analiza el búfer y lo sobrescribe my-functionen la memoria porque si yo No ejecutar C-x C-e M-xejecuta la función desde la última vez que ejecutéC-x C-e
jrh
Parece que C-x C-e evalúa el búfer , parece que el resultado de evaluar un búfer que contiene a defunes registrar la función, aunque este artículo parece hacerme pensar que debería ejecutar la función, y sin embargo, lo único que se muestra en el minibúfer es my-function(¿implica que devuelve la función?), no This is a great function. Debo estar perdiendo algo aquí.
jrh
1
Gracias por tus comentarios, @jrh. Esta pregunta y respuesta abordan un aspecto particular de elisp, cómo hacer que una función sea interactiva (es decir, convertir la función en un comando). Estás preguntando sobre aspectos más fundamentales de elisp, y estás más allá del alcance de esta pregunta. Recomiendo trabajar con la introducción de Emacs Lisp. Parece confundido acerca de la diferencia entre evaluar una definición de función, que (re) define una función pero en realidad no la llama, y ​​llamar a la función, que ejecuta el código de la función.
Tyler