Emacs - Wyłącz niektóre wiadomości z minibufora

20

W Emacsie jest kilka przypadków, w których chciałbym zapobiec pojawianiu się wiadomości w minibuforze, głównie dotyczących „Początku / końca bufora” i „Tekst jest tylko do odczytu”.

Czy jest jakiś sposób, aby zapobiec pojawianiu się tych wiadomości w minibuforze?

Czy jest jakiś ważny powód, dla którego mógłbym nie chcieć ich wyłączać? Na wartość nominalną mogę równie łatwo spojrzeć na numer wiersza i status zapisu buforu na modeline.

bitflipy
źródło
2
Nie ma powodu, dla którego potrzebujesz tych wiadomości, nie. Powodem istnienia tych komunikatów jest próba upewnienia się, że każde polecenie ma jakiś widoczny efekt: gdy nie można wykonać oczekiwanego widocznego efektu polecenia, zamiast tego emitujemy komunikat, dzięki czemu można stwierdzić, że polecenie rzeczywiście zostało uruchomione.
Stefan

Odpowiedzi:

21

W Emacsie 25 możesz ukryć komunikaty minibufora, wiążąc się inhibit-messagez wartością inną niż zero:

(let ((inhibit-message t))
  (message "Listen to me, you!"))
Jackson
źródło
Czy działa to również na prymitywach wywoływanych również z C?
Aaron Miller,
1
Powinien, jak message1wywołuje funkcja C message3, szanować tę zmienną.
Jackson,
Przydatny do tłumienia irytującego komunikatu mu4e „Retreiving mail ...”:(let ((inhibit-message t)) (message make-progress-reporter))
manandearth
1
Dziwne, że to nie działa w Emacsie 26.1. Masz pomysł, dlaczego?
Christian Hudon
1
@ChristianHudon Właśnie przetestowałem w Emacs 26.1 i master bez pliku init, i działa dla mnie w obu miejscach. Zauważ, że messagezwraca ciąg komunikatu, więc być może widzisz zwrócony ciąg podczas oceny kodu. Jeśli ewaluujesz ten kod w skrócie klawiszowym, żadna wiadomość nie zostanie wydrukowana (z wyjątkiem bufora Wiadomości ).
Jackson
9

Można rodzaj zrobić to z kodu Lisp. Dlaczego „w pewnym sensie”? Ponieważ MESSAGE jest prymitywem zdefiniowanym w C, zamiast funkcji Lisp, i zgodnie z podręcznikiem Emacsa Lispa , wywołania prymitywów z kodu C ignorują porady.

Dlatego, aby naprawdę dobrze wykonać pożądaną funkcjonalność, musisz ponownie zdefiniować prymityw MESSAGE jako funkcję Lisp; kiedy już to zrobisz, możesz doradzić kodem, który otrzymuje ciąg MESSAGE, który echo przekazuje do minibufora, porównuje go z listą wiadomości, których nie chcesz widzieć, a następnie wywołuje lub nie wywołuje MESSAGE w zależności na wynik. Teoretycznie można to osiągnąć np. (defvar *message-prim* (symbol-function 'message)), A następnie (defun message (format &rest args) ... (funcall *message-prim* format args))- ale funkcja SYMBOL podana w pierwotnym argumencie zwraca coś, co w rzeczywistości nie jest możliwe do wywołania, więc FUNCALL sygnalizuje warunek VOID-FUNCTION.

Jednak nawet gdyby to zadziałało, nadal nie dałoby rady, ponieważ ponowne zdefiniowanie prymitywu gwarantuje tylko, że redefinicja zostanie użyta, gdy funkcja zostanie wywołana z kodu Lisp; wywołania w kodzie C mogą nadal używać pierwotnej definicji . (Kod C może wywoływać Emacs Lisp, a takie przypadki zobaczą redefinicję; możliwe jest również, że kod C wywoła kod C, a takie przypadki zobaczą oryginalną definicję.)

Zastanawiam się nad łataniem kodu C i ponownej kompilacji Emacsa, aby zapewnić odpowiednią funkcję tłumienia wiadomości; Tak naprawdę nie potrzebuję tej funkcjonalności, ale może okazać się ciekawym ćwiczeniem, zwłaszcza że nie jestem hakerem C. W międzyczasie przygotowałem coś, co po upuszczeniu do pliku, dołączonego do jednego z twoich plików init i dostosowanego do twojego gustu, ukryje wiadomości pochodzące z kodu Lisp, które dokładnie pasują do napisów, które chcesz pominąć. Dopóki włączone jest tłumienie, komunikaty te nigdy nie pojawią się w minibuforze; masz opcję, czy je również *Messages*usunąć z bufora.

;; message-suppression.el
;; a quick hack by Aaron ([email protected]), 2013-11-12
;; half a solution for http://superuser.com/questions/669701/emacs-disable-some-minibuffer-messages
;; NB this does nothing until you 
;; M-x customize-group RET message-suppression RET
;; and adjust to taste

(defgroup message-suppression nil
  "Customization options for selective message suppression."
  :prefix "message-suppression")

(defcustom message-suppression-enabled nil
  "Whether or not to suppress messages listed in
`message-suppress-these'."
  :group 'message-suppression
  :tag "Suppress some messages?"
  :type '(choice (const :tag "No" nil)
                 (const :tag "Yes" t)))

(defcustom message-suppression-to-messages-buffer t
  "Whether or not to insert messages suppressed from the
minibuffer into the *Messages* buffer."
  :group 'message-suppression
  :tag "Insert suppressed messages into *Messages* buffer?"
  :type '(choice (const :tag "No" nil)
                 (const :tag "Yes" t)))

(defcustom message-suppression-these nil
  "A list of messages which the `message-except-these' advice
should suppress from being echoed in the minibuffer. Messages
are matched by `member', i.e., only exact strings match.

NB! Per the Emacs manual, calls from C code to primitives (such
as `message') ignore advice entirely, which means some messages
cannot be suppressed by this mechanism. ('Advising
Functions' in the Emacs Lisp Reference Manual, q.v.)"
  :group 'message-suppression
  :tag "Messages to suppress"
  :type '(repeat (string))
  :link '(info-link "(elisp)Advising Functions"))

(defadvice message (around message-suppress-advice)
  "Suppress messages listed in `message-suppress-these' from being
  echoed in the minibuffer."
  (let ((message-string nil)
        (current-buffer nil))
    (if (and message-suppression-enabled
             (length (ad-get-args 0))
             (stringp (car (ad-get-args 0)))
             ;; message-string doesn't get set until here because `format'
             ;; will complain if its first argument isn't a string
             (setq message-string (apply 'format (ad-get-args 0)))
             (member message-string
                     message-suppression-these))
        ;; we won't call `message', but we might echo to *Messages*
        (and message-suppression-to-messages-buffer
             (progn
               (setq current-buffer (current-buffer))
               (switch-to-buffer (get-buffer-create "*Messages*"))
               (goto-char (point-max))
               (insert (make-string 1 10))
               (insert message-string)
               (switch-to-buffer current-buffer)))
      ad-do-it)))

(ad-activate 'message)

Przetestowałem to do pracy z komunikatami, które faktycznie są generowane z kodu Lisp, np. Skarga „Nie określiłeś funkcji” powtórzona przez DESCRIBE-FUNCTION, gdy podajesz pusty argument ciągu. Niestety wiadomości, które chcesz ukryć, takie jak „Początek bufora”, „Koniec bufora” i „Tekst jest tylko do odczytu”, wydają się pochodzić z kodu C, co oznacza, że ​​nie będziesz w stanie tłumić je tą metodą.

Jeśli kiedykolwiek przejdę do łatki źródłowej, będzie (prawdopodobnie) przeciwko Emacsowi 24.3 i zaktualizuję tę odpowiedź o informacje o tym, jak z niej korzystać.

Aaron Miller
źródło
8

W Emacsie 25 i prawdopodobnie we wcześniejszych wersjach najczystszy sposób to zrobić:

Najpierw zdefiniuj:

(defun suppress-messages (old-fun &rest args)
  (cl-flet ((silence (&rest args1) (ignore)))
    (advice-add 'message :around #'silence)
    (unwind-protect
         (apply old-fun args)
      (advice-remove 'message #'silence))))

Następnie, jeśli chcesz ukryć wszystkie produkowane przez some-functionciebie wiadomości :

(advice-add 'some-function :around #'suppress-messages)

Na przykład pomijam komunikat „Proces Ispell zabity” wywołany przez funkcję ispell-kill-ispell(in ispell.el.gz), pisząc:

(advice-add 'ispell-kill-ispell :around #'suppress-messages)

Jeśli kiedykolwiek będziesz musiał ponownie włączyć wiadomości, uruchom:

(advice-remove 'some-function #'suppress-messages)

Kilka rzeczy do zapamiętania:

1) Wszystkie komunikaty generowane przez some-functionbędą pomijane, podobnie jak wszystkie komunikaty generowane przez dowolną funkcję lisp wywoływaną przez funkcję.

2) Wiadomości generowane przez kod C nie będą tłumione, ale to chyba wszystko, co najlepsze.

3) Musisz upewnić się, że -*- lexical-binding: t -*-znajduje się w pierwszym wierszu .elpliku.

Ale jak dowiedzieć się, która funkcja jest wywoływana message? Możesz przeglądać kod, jak sugerował ktoś inny, ale łatwiej jest pozwolić Emacsowi wykonać pracę za Ciebie.

Jeśli zdefiniujesz:

(defun who-called-me? (old-fun format &rest args)
  (let ((trace nil) (n 1) (frame nil))
      (while (setf frame (backtrace-frame n))
        (setf n     (1+ n) 
              trace (cons (cadr frame) trace)) )
      (apply old-fun (concat "<<%S>>\n" format) (cons trace args))))

a następnie wykonaj:

(advice-add 'message :around #'who-called-me?)

otrzymasz wiadomość wsteczną dodaną do wiadomości. Dzięki temu możesz łatwo zobaczyć, gdzie wiadomość została wygenerowana.

Możesz to odwrócić za pomocą:

(advice-remove 'message #'who-called-me?)

Alternatywnym podejściem byłoby doradzenie messagefunkcji i przetestowanie, czy chcesz wydrukować wiadomość, czy nie. Jest to proste, jeśli wiadomość jest stałym ciągiem. Np. Aby ukryć „proces Ispell zabity”, możesz zdefiniować:

(defun suppress-ispell-message (old-fun format &rest args)
  (if (string= format "Ispell process killed")
         (ignore)
    (apply old-fun format args)))

a następnie wykonaj:

(advice-add 'message :around #'suppress-ispell-message)

Takie podejście wkrótce staje się bardzo nieuporządkowane, jeśli wiadomość jest czymś skomplikowanym.

Bernard Hurley
źródło
3

Najwyraźniej pytasz o sposób selektywnego blokowania niektórych wiadomości. Odpowiedzią na to jest to, że musiałbyś przedefiniować lub doradzić kodowi, który emituje te konkretne komunikaty.

Aby zapobiec wszystkim wiadomościom, na przykład na czas trwania jakiegoś kodu, możesz użyć fletlub cl-fletponownie zdefiniować funkcję messagelokalnie na (funkcja) ignore. Lub użyj techniki użytej w edt-electric-helpify: zapisz oryginalną definicję message, fsetaby ignore, ponowniefset ją do pierwotnego def (lepiej, unwind-protectjeśli to zrobisz).

Rysował
źródło
Przepraszamy, ale czy mógłbyś mi pomóc w wyszukiwaniu tych komunikatów o błędach? W tym momencie wydaje mi się, że trudniej jest wyłączyć wiadomości niż je zatrzymać.
bitflips
1
Aby wyszukać „te komunikaty o błędach”, użyj greplub Aw Dired. Wyszukaj tekst komunikatu o błędzie w plikach źródłowych Emacs Lisp (i ewentualnie także w plikach Emacs C, jeśli są dostępne). HTH.
Drew
2

Działa to w celu pomijania „Początku bufora” i „Koniec bufora” i nie wymaga emacsa 25.

; Suppress "Beginning of buffer" and "End of buffer" messages
(defadvice previous-line (around silencer activate)
  (condition-case nil
    ad-do-it
    ((beginning-of-buffer))))

(defadvice next-line (around silencer activate)
  (condition-case nil
    ad-do-it
    ((end-of-buffer))))

Zainspirowany https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html, ale używa „defadvice” dla większej kompatybilności.

trogdoro
źródło