Dlaczego nie mogę powiązać mojej funkcji z klawiszem lub wywołać go za pomocą Mx?

13

Napisałem funkcję i chcę ją wywołać za pomocą Mx i przypisać do klawisza. To moja funkcja:

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

Jeśli spróbuję to wywołać M-x my-function, pojawia się błąd: [no match]w mini-buforze.

Jeśli spróbuję powiązać go z klawiszem (lub kliknięciem myszy):

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

Wygląda na to, że działa, ale gdy próbuję to wywołać C-c a, pojawia się błąd

Nieprawidłowy typ argumentu: polecenie, moja funkcja

Dlaczego nie mogę korzystać z mojej funkcji?

Tyler
źródło
5
Podaję to pytanie jako ogólną odpowiedź na często zadawane pytania na ten temat. Zachęcamy do rozszerzenia lub wyjaśnienia, jednak sensowne jest, aby pytanie i odpowiedź były wykrywalne i przydatne dla osób mających podobne problemy!
Tyler,
1
Dzięki, że to zrobiłeś, Tyler. Zaznaczyłem Q dla moderatora, aby przekonwertować to na pytanie społeczności.
Drew,
Zastanawiam się tylko, czy tytuł powinien po prostu zacytować komunikat o błędzie. Może to być łatwiejsze do znalezienia dla ludzi i może pozwolić na odpowiedzi, które nie mają nic wspólnego z samym dodawaniem interactive- np. Czasami polecenie znika z nowej wersji biblioteki. Błąd może zostać zgłoszony w dowolnym kontekście, w którym Emacs oczekuje polecenia.
Drew,
Byłoby dobrze, gdyby ludzie przeszukiwali teraz wiki, powiedzmy, commandpaby spróbować znaleźć inne pytania, które można zamknąć jako duplikaty tego. Należy jednak uważnie przeczytać pytania i odpowiedzi, ponieważ niektóre są różne. W niektórych przypadkach warto powtórzyć odpowiedź (i kontekst Q). W innych przypadkach pytanie nie ma związku i należy je pozostawić takie, jakie jest (nie zamknięte).
Drew,
1
@Drew Nie byłem pewien, jak najlepiej „zareklamować” to w tytule. Błąd polecenia pojawia się tylko wraz ze skrótami klawiszowymi, więc ludzie biegający w tym kierunku z kierunku Mx nie zobaczą połączenia. Nie jestem pewien, jaka jest najlepsza równowaga między jasnym, zwięzłym tytułem a tytułem, który pojawi się dla wszystkich odpowiednich zapytań. Dostosuj, jak chcesz!
Tyler,

Odpowiedzi:

22

Najważniejsze jest to, że istnieje różnica między funkcją a poleceniem .

W Emacs lisp funkcje nie są domyślnie wywoływane interaktywnie. Oznacza to, że nie można uzyskać do nich dostępu za M-xpomocą klawisza lub kliknięcia myszy ani przypisać ich. Jeśli chcesz to zrobić, musisz jawnie zadeklarować funkcję interactive, którą wykonujesz, dodając (interactive)formularz jako pierwszy wiersz w treści (po ciągu dokumentacji). Funkcja interaktywna nazywana jest poleceniem. Wyjaśniono to w instrukcji: (info "(elisp) Using Interactive") (wersja online) .

Wyświetlany komunikat o błędzie Wrong type argument: commandp, my-functionwskazuje, że próbujesz wywołać funkcję interaktywnie, ale ta funkcja nie jest poleceniem .

Aby wyjaśnić rzeczywisty błąd, litera pjest często używana w lisp do wskazania predykatu lub testu. W tym przypadku Emacs sprawdza, my-functionczy jest to polecenie korzystające z testu commandp. Nie jest, co prowadzi do błędu. Podobne błędy pojawiają się przy każdym użyciu przedmiot niewłaściwego typu: jeśli Emacs oczekuje ciąg i przekazać symbol, może pojawić się odniesienie do stringp, na przykład.

Aby odpowiedzieć na zadane pytanie, musisz dodać (interactive)wiersz do definicji:

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

Istnieje wiele opcji interactiveformularza, obsługujących wszelkiego rodzaju sposoby przekazywania informacji do funkcji. Sprawdź w instrukcji wszystkie szczegóły.

Makra klawiaturowe są szczególnym przypadkiem w tym kontekście. Makro klawiatury to sekwencja zdarzeń wejściowych, reprezentowana jako ciąg znaków. Makra klawiaturowe zachowują się jak polecenia, dzięki czemu można je przypisywać do klawiszy, nie martwiąc się o dodanie interactivedeklaracji. Na przykład w następujący sposób:

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

"λ"to makro klawiatury, dzięki czemu możemy go C-c lbez problemu powiązać . Jeśli staramy się zrobić to samo z funkcją musimy mieć pewność, aby zdefiniować funkcję jako 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
źródło
jedną sugestię, którą chciałbym dodać, wyjaśnij, że musisz zrobić Cx Ce przed zrobieniem tego M-x my-functionw swoim przykładzie. Również jako początkujący emacs nie jestem jeszcze w 100% pewien, co dokładnie C-x C-erobi lub kiedy trzeba go uruchomić, ale wydaje się, że ... kiedy go uruchomisz, analizuje bufor i zastępuje my-functionw pamięci, ponieważ jeśli ja nie uruchamiaj C-x C-e M-xuruchamia funkcję od ostatniego uruchomieniaC-x C-e
jrh
Wygląda na to, że C-x C-e ocenia bufor , wydaje się, że wynikiem oceny buforu zawierającego a defunjest zarejestrowanie funkcji, chociaż ten artykuł wydaje mi się, że myślę, że powinien on po prostu uruchomić funkcję, a jednak jedyną rzeczą pokazaną w minibuforze jest my-function(co oznacza, że ​​zwraca funkcję?), nie This is a great function. Coś mi tu brakuje.
jrh
1
Dzięki za komentarze, @jrh. To pytanie i odpowiedź dotyczą konkretnego aspektu elisp, jak uczynić funkcję interaktywną (tj. Zamienić funkcję w polecenie). Pytasz o bardziej podstawowe aspekty elisp i wykraczasz poza to pytanie. Polecam zapoznanie się z Emacs Lisp Intro. Wydaje się, że jesteś zdezorientowany co do różnicy między oceną definicji funkcji, która (ponownie) definiuje funkcję, ale tak naprawdę jej nie wywołuje, a wywołaniem funkcji, która uruchamia kod funkcji.
Tyler,