Por que não consigo vincular minha função a uma tecla ou chamá-la com Mx?

13

Eu escrevi uma função e quero chamá-la via Mx e vinculá-la a uma chave. Esta é a minha função:

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

Se eu tentar chamá-lo M-x my-function, recebo o erro: [no match]no mini-buffer.

Se eu tentar vinculá-lo a uma chave (ou clique do mouse):

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

Parece funcionar, mas quando tento chamá-lo C-c a, recebo o erro

Argumento de tipo errado: commandp, my-function

Por que não consigo usar minha função?

Tyler
fonte
5
Eu ofereço esta pergunta como uma resposta genérica às perguntas freqüentes sobre esse assunto. Sinta-se livre para expandir ou esclarecer, no entanto, faz sentido tornar a pergunta e a resposta detectáveis ​​e úteis para pessoas com problemas semelhantes!
Tyler
1
Obrigado por fazer isso, Tyler. Sinalizei o Q de um moderador para converter isso em uma pergunta da comunidade.
Tirou
Uma coisa que me pergunto é se o título deve apenas citar a mensagem de erro. Isso pode ser mais fácil para as pessoas encontrarem, e pode permitir respostas que não têm a ver apenas com a adição interactive- por exemplo, às vezes um comando desaparece de uma nova versão de uma biblioteca. O erro pode ser gerado em qualquer contexto em que o Emacs espera um comando.
Tirou
Seria bom que as pessoas agora pesquisassem no wiki, digamos commandp, para tentar encontrar outros Qs que possam ser fechados como duplicados deste. Porém, tenha cuidado ao ler as perguntas e respostas, pois algumas são diferentes. Em alguns casos, vale a pena repetir a resposta (e o contexto Q) aqui. Em outros casos, a questão não é relacionada e deve ser deixada como está (não fechada).
Tirou
1
@ Drew Eu não tinha certeza da melhor forma de 'anunciar' isso no título. O erro commandp aparece apenas com as combinações de teclas, portanto, as pessoas que se deparam com isso na direção Mx não veem a conexão. Não sei qual é o melhor equilíbrio entre um título claro e conciso e um título que será exibido para todas as consultas de pesquisa relevantes. Sinta-se livre para ajustar como quiser!
Tyler

Respostas:

22

O ponto principal é que há uma diferença entre uma função e um comando .

No Emacs lisp, as funções não são chamadas interativamente por padrão. Isso significa que você não pode acessá-los via M-xou vinculá-los a uma tecla ou clique do mouse. Se você quiser fazer isso, precisará declarar explicitamente a função interactive, adicionando um (interactive)formulário como a primeira linha do corpo (seguindo a sequência de documentação). Uma função interativa é chamada de comando. Isso é explicado no manual: (info "(elisp) Using Interactive") (versão online) .

A mensagem de erro exibida Wrong type argument: commandp, my-functionindica que você está tentando chamar uma função interativamente, mas essa função não é um comando .

Para explicar o erro real, a letra pé frequentemente usada no lisp para indicar um predicado ou teste. Nesse caso, o Emacs está testando my-functionpara ver se é um comando usando o teste commandp. Não é, o que leva ao erro. Erros semelhantes aparecem sempre que você usa um objeto do tipo errado: se o Emacs espera uma string e passa um símbolo, você pode ver uma referência a stringp, por exemplo.

Para responder à pergunta conforme solicitado, você precisa adicionar a (interactive)linha à definição:

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

Existem muitas opções para o interactiveformulário, suportando todos os tipos de maneiras de passar informações para sua função. Verifique o manual para todos os detalhes.

As macros do teclado são um caso especial nesse contexto. Uma macro de teclado é uma sequência de eventos de entrada, representada como uma sequência. As macros do teclado se comportam como comandos, para que você possa vinculá-las às teclas sem se preocupar em adicionar uma interactivedeclaração. Por exemplo, no seguinte:

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

"λ"é uma macro de teclado, para que possamos vinculá-la C-c lsem problemas. Se tentarmos fazer o mesmo com uma função, precisamos definir a função 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
fonte
gostaria de acrescentar uma sugestão, deixe claro que você precisa fazer o Cx Ce antes de fazer M-x my-functionno seu exemplo. Também como iniciante no emacs, ainda não estou 100% claro sobre o que exatamente C-x C-efaz ou quando você precisa executá-lo, mas parece que ... quando você o executa, ele analisa o buffer e sobrescreve my-functionna memória, porque se eu não execute C-x C-e M-xexecuta a função a partir da última vez que eu corriC-x C-e
JRH
Parece que C-x C-e avalia o buffer , parece que o resultado da avaliação de um buffer contendo a defunestá registrando a função, embora este artigo pareça me fazer pensar que ele deve apenas executar a função e, no entanto, a única coisa mostrada no minibuffer é my-function(implicando que ele retorna a função?), não This is a great function. Eu devo estar esquecendo algo aqui.
Jrh 21/10
1
Obrigado pelos seus comentários, @jrh. Esta pergunta e resposta estão abordando um aspecto particular do elisp, como tornar uma função interativa (por exemplo, transformar função em um comando). Você está perguntando sobre aspectos mais fundamentais do elisp e está além do escopo desta pergunta. Eu recomendo trabalhar com o Emacs Lisp Intro. Você parece estar confuso sobre a diferença entre avaliar uma definição de função, que (re) define uma função, mas na verdade não a chama, e chama a função, que executa o código da função.
Tyler