Emacs: disabilita alcuni messaggi del minibuffer


20

In Emacs, ci sono alcuni casi in cui vorrei impedire la visualizzazione dei messaggi nel minibuffer, principalmente relativi a "Inizio / Fine del buffer" e "Il testo è di sola lettura".

Esiste un modo per impedire che questi messaggi compaiano nel minibuffer?

Inoltre, c'è qualche motivo significativo per cui potrei non voler disabilitarli? Per quanto riguarda il valore nominale, posso altrettanto facilmente guardare il numero di riga e lo stato di scrittura del buffer sulla modeline.


2
Non c'è motivo per cui avresti bisogno di quei messaggi, no. Il motivo per cui esistono questi messaggi è cercare di assicurarsi che ogni comando abbia un effetto visibile: quando l'effetto visibile previsto del comando non può essere eseguito, invece emettiamo un messaggio, in modo da poter dire che il comando è stato effettivamente eseguito.
Stefan

Risposte:


21

In Emacs 25, puoi sopprimere i messaggi del minibuffer associandoli inhibit-messagea un valore diverso da zero:

(let ((inhibit-message t))
  (message "Listen to me, you!"))

Funziona anche su primitivi chiamati da C?
Aaron Miller,

1
Dovrebbe, come message1chiama la funzione C message3, che rispetta questa variabile.
Jackson,

Utile per sopprimere il fastidioso messaggio mu4e "Retreiving mail ...":(let ((inhibit-message t)) (message make-progress-reporter))
manandearth

1
Questo non funziona per me su Emacs 26.1, stranamente. Qualche idea sul perché?
Christian Hudon,

1
@ChristianHudon Ho appena provato in Emacs 26.1 e master senza file init, e funziona in entrambi i casi. Nota che messagerestituisce la stringa di messaggio, quindi forse stai vedendo la stringa restituita durante la valutazione del codice. Se si valuta questo codice in una combinazione di tasti, non verrà stampato alcun messaggio (tranne nel buffer Messaggi ).
Jackson,

9

Si può sorta di farlo da codice Lisp. Perché "sorta di"? Perché MESSAGE è una primitiva, definita in C, anziché una funzione Lisp, e, secondo il manuale di riferimento di Emacs Lisp , le chiamate alle primitive dal codice C ignorano i consigli.

Pertanto, al fine di svolgere davvero un lavoro adeguato nell'implementazione della funzionalità desiderata, è necessario ridefinire la primitiva MESSAGE come funzione Lisp; una volta che lo hai fatto, puoi quindi avvisarlo con un codice che ottiene la stringa MESSAGGIO che echeggerebbe al minibuffer, lo confronta con un elenco di messaggi che non vuoi vedere, quindi chiama o non chiama MESSAGGIO a seconda sul risultato. In teoria, questo potrebbe essere realizzato per esempio (defvar *message-prim* (symbol-function 'message)), e poi (defun message (format &rest args) ... (funcall *message-prim* format args))- ma SYMBOL-FUNCTION dato un argomento primitivo restituisce qualcosa che non è realmente richiamabile, quindi FUNCALL segnala una condizione VOID-FUNCTION.

Tuttavia, anche se funzionasse, non farebbe davvero il trucco, perché ridefinire una primitiva garantisce solo che la ridefinizione verrà utilizzata quando la funzione viene chiamata dal codice Lisp; le chiamate in codice C possono comunque utilizzare la definizione primitiva . (È possibile che il codice C chiami in Emacs Lisp e tali casi vedranno la ridefinizione; è anche possibile che il codice C chiami il codice C e tali casi vedranno la definizione originale.)

Sto vagamente pensando di correggere il codice C e ricompilare Emacs per fornire la corretta funzionalità di soppressione dei messaggi; Non ho davvero bisogno di quella funzionalità, ma potrebbe rivelarsi un esercizio interessante, soprattutto perché non sono un hacker C. Nel frattempo, ecco qualcosa che ho preparato che, quando rilasciato in un file, incluso in uno dei tuoi file init e personalizzato secondo i tuoi gusti, sopprimerà i messaggi provenienti dal codice Lisp che corrispondono esattamente alle stringhe che elenchi per la soppressione. Finché la soppressione è abilitata, questi messaggi non appariranno mai nel minibuffer; hai la possibilità di eliminarli anche dal *Messages*buffer.

;; message-suppression.el
;; a quick hack by Aaron (me@aaron-miller.me), 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)

Ho provato questo per funzionare con i messaggi che sono effettivamente generati dal codice Lisp, ad esempio il reclamo "Non hai specificato una funzione" echeggiato da DESCRIBE-FUNCTION quando gli dai un argomento stringa vuoto. Sfortunatamente, i messaggi che dici di voler sopprimere, come "Inizio del buffer", "Fine del buffer" e "Il testo è di sola lettura", sembrano tutti originati dal codice C, il che significa che non sarai in grado di sopprimili con questo metodo.

Se mai dovessi aggirare la patch di origine, sarà (probabilmente) contro Emacs 24.3 e aggiornerò questa risposta con informazioni su come procedere usando.


8

In Emacs 25 e probabilmente in alcune versioni precedenti, il modo più pulito per farlo è il seguente:

Per prima cosa definire:

(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))))

Quindi se vuoi sopprimere tutti i messaggi da some-functionte prodotti fai:

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

Ad esempio, sopprimo il messaggio "Processo Ispell ucciso" prodotto dalla funzione ispell-kill-ispell(in ispell.el.gz) scrivendo:

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

Se hai mai bisogno di riattivare i messaggi, esegui:

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

Alcune cose da notare:

1) Tutti i messaggi prodotti some-functionsaranno soppressi così come tutti i messaggi prodotti da qualsiasi funzione lisp chiamata dalla funzione.

2) I messaggi prodotti dal codice C non verranno soppressi, ma probabilmente è tutto per il meglio.

3) È necessario assicurarsi che -*- lexical-binding: t -*-sia contenuto nella prima riga del .elfile.

Ma come scopri quale funzione ha chiamato message? Potresti consultare il codice come suggerito da qualcun altro, ma è più facile lasciare che Emacs faccia il lavoro per te.

Se si definisce:

(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))))

e poi fai:

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

otterrai un backtrace aggiunto al messaggio. Da questo puoi vedere facilmente dove è stato generato il messaggio.

Puoi invertire questo con:

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

Un approccio alternativo sarebbe quello di consigliare la messagefunzione e provare per vedere se si desidera stampare il messaggio o meno. Questo è semplice se il messaggio in questione è una stringa fissa. Ad esempio per sopprimere "Processo Ispell ucciso" potresti definire:

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

e poi fai:

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

Questo approccio diventa presto molto complicato se il messaggio è qualcosa di complicato.


3

Apparentemente stai chiedendo un modo per inibire selettivamente determinati messaggi. La risposta è che dovresti ridefinire o consigliare il codice che emette quei messaggi particolari .

Per impedire tutti i messaggi, ad esempio per la durata di alcuni codici, è possibile utilizzare fleto cl-fletridefinire la funzione messagelocalmente su (funzione) ignore. Oppure usa la tecnica usata in edt-electric-helpify: salva la definizione originale di message, fsetin ignore, fsettorna alla def originale (anche se è meglio usarla unwind-protectse lo fai).


Mi dispiace, ma saresti in grado di aiutarmi su come posso cercare questi messaggi di errore? A questo punto, sembra quasi che sia più difficile disabilitare i messaggi che non trattenerli.
Bitflip il

1
Per cercare "questi messaggi di errore", utilizzare grepo Ain Dired. Cerca il testo del messaggio di errore nei file sorgente di Emacs Lisp (e possibilmente anche nei file Emacs C, se li hai disponibili). HTH.
Estratto il

2

Funziona per sopprimere "Inizio del buffer" e "Fine del buffer" e non richiede emacs 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))))

Ispirato da https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html ma utilizza "defadvice" per una maggiore compatibilità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.