Emacs: deshabilite algunos mensajes de minibúfer

20

En Emacs, hay algunos casos en los que me gustaría evitar que aparezcan mensajes en el minibúfer, principalmente relacionados con "Principio / Fin del búfer" y "El texto es de solo lectura".

¿Hay alguna forma de evitar que estos mensajes aparezcan en el minibúfer?

Además, ¿hay alguna razón importante por la que no quiera deshabilitarlos? A primera vista, puedo ver fácilmente el número de fila y el estado de escritura del búfer en la línea de modo.

bitflips
fuente
2
No hay razón para que necesite esos mensajes, no. La razón por la que existen esos mensajes es para intentar asegurarse de que cada comando tenga algún efecto visible: cuando no se puede ejecutar el efecto visible esperado del comando, en su lugar emitimos un mensaje, por lo que puede decir que el comando se ejecutó.
Stefan

Respuestas:

21

En Emacs 25, puede suprimir los mensajes del minibúfer al vincular inhibit-messagea un valor que no sea nulo:

(let ((inhibit-message t))
  (message "Listen to me, you!"))
Jackson
fuente
¿Funciona esto también en primitivas llamadas desde C?
Aaron Miller
1
Debería, como message1llama la función C message3, respetar esta variable.
Jackson
Útil para suprimir el molesto mensaje de mu4e "Recuperando correo ...":(let ((inhibit-message t)) (message make-progress-reporter))
manandearth
1
Esto no funciona para mí en Emacs 26.1, por extraño que parezca. ¿Alguna idea de por qué?
Christian Hudon
1
@ChristianHudon Acabo de probar en Emacs 26.1 y master sin archivo de inicio, y funciona para mí en ambos lugares. Tenga en cuenta que messagedevuelve la cadena del mensaje, por lo que tal vez esté viendo la cadena devuelta al evaluar el código. Si evalúa este código en una combinación de teclas, no se imprimirá ningún mensaje (excepto en el búfer de Mensajes ).
Jackson
9

Puede especie de hacerlo desde el código Lisp. ¿Por qué "tipo de"? Debido a que MESSAGE es una primitiva, definida en C, en lugar de una función Lisp, y, según el manual de referencia de Emacs Lisp , las llamadas a primitivas desde el código C ignoran el consejo.

Por lo tanto, para realmente hacer un trabajo adecuado de implementación de la funcionalidad que desea, necesitaría redefinir la primitiva MENSAJE como una función Lisp; una vez que lo haya hecho, puede aconsejarlo con un código que obtenga la cadena que MENSAJE haría eco en el minibúfer, lo compara con una lista de mensajes que no desea ver y luego llama o no llama MENSAJE dependiendo en el resultado En teoría, esto podría lograrse, por ejemplo (defvar *message-prim* (symbol-function 'message)), y luego (defun message (format &rest args) ... (funcall *message-prim* format args)), pero SYMBOL-FUNCTION dado un argumento primitivo devuelve algo que en realidad no es invocable, por lo que FUNCALL señala una condición de VOID-FUNCTION.

Sin embargo, incluso si eso funcionara, todavía no funcionaría, porque redefinir una primitiva solo garantiza que la redefinición se utilizará cuando la función se llame desde el código Lisp; las llamadas en código C aún pueden usar la definición primitiva . (Es posible que el código C llame a Emacs Lisp, y tales casos verán la redefinición; también es posible, por supuesto, que el código C llame al código C, y estos casos verán la definición original).

Estoy contemplando vagamente parchear el código C y recompilar Emacs para proporcionar la funcionalidad adecuada de supresión de mensajes; Realmente no necesito esa funcionalidad, pero podría resultar un ejercicio interesante, especialmente porque no soy un hacker de C. Mientras tanto, he aquí algo que preparé y que, cuando lo coloco en un archivo, incluido en uno de sus archivos de inicio y personalizado a su gusto, suprimirá los mensajes que se originan en el código Lisp que coinciden exactamente con las cadenas que enumera para supresión. Mientras la supresión esté habilitada, estos mensajes nunca aparecerán en el minibúfer; también tiene la opción de suprimirlos del *Messages*búfer.

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

He probado esto para que funcione con mensajes que en realidad se generan a partir del código Lisp, por ejemplo, la queja "No especificó una función" reflejada por DESCRIBE-FUNCTION cuando le da un argumento de cadena vacía. Desafortunadamente, los mensajes que menciona que desea suprimir, como "Inicio del búfer", "Fin del búfer" y "El texto es de solo lectura", parecen originarse en el código C, lo que significa que no podrá suprimirlos por este método.

Si alguna vez llego al parche de origen, (probablemente) estará en contra de Emacs 24.3 , y actualizaré esta respuesta con información sobre cómo usarlo.

Aaron Miller
fuente
8

En Emacs 25 y probablemente en algunas versiones anteriores, la forma más limpia de hacerlo es la siguiente:

Primero defina:

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

Luego, si desea suprimir todos los mensajes producidos por some-functionusted, haga lo siguiente:

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

Por ejemplo, suprimo el mensaje "Proceso de Ispell eliminado" producido por la función ispell-kill-ispell(en ispell.el.gz) escribiendo:

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

Si alguna vez necesita volver a habilitar los mensajes, ejecute:

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

Algunas cosas a tener en cuenta:

1) Todos los mensajes producidos por some-functionserán suprimidos al igual que todos los mensajes producidos por cualquier función de lisp a la que llame la función.

2) Los mensajes producidos por el código C no se suprimirán, pero eso probablemente sea todo lo mejor.

3) Debe asegurarse de que -*- lexical-binding: t -*-esté contenido en la primera línea de su .elarchivo.

Pero, ¿cómo saber qué función llama message? Podrías revisar el código como alguien más sugirió, pero es más fácil dejar que Emacs haga el trabajo por ti.

Si define:

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

y luego hacer:

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

obtendrá una traza inversa agregada al mensaje. A partir de esto, puede ver fácilmente dónde se generó el mensaje.

Puedes revertir esto con:

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

Un enfoque alternativo sería aconsejar la messagefunción y la prueba para ver si desea imprimir el mensaje o no. Esto es simple si el mensaje en cuestión es una cadena fija. Por ejemplo, para suprimir el "proceso de Ispell eliminado", podría definir:

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

y luego hacer:

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

Este enfoque pronto se vuelve muy complicado si el mensaje es algo complicado.

Bernard Hurley
fuente
3

Aparentemente, está solicitando una forma de inhibir selectivamente ciertos mensajes. La respuesta para eso es que necesitaría redefinir o aconsejar el código que emite esos mensajes en particular .

Para evitar todos los mensajes, por ejemplo, durante la duración de algún código, puede usar fleto cl-fletredefinir la función messagelocalmente en (función) ignore. O utilice la técnica utilizada en edt-electric-helpify: guardar la definición original de message, fsetpara ignore, fsetvolver a la definición original (aunque es mejor usarla unwind-protectsi lo hace).

Dibujó
fuente
Lo sentimos, pero ¿podría ayudarme en cómo puedo buscar estos mensajes de error? En este punto, casi parece que es más difícil deshabilitar los mensajes que retenerlos.
bitflips
1
Para buscar "estos mensajes de error", use grepo Aen Dired. Busque el texto del mensaje de error en los archivos de origen de Emacs Lisp (y posiblemente también en los archivos de Emacs C, si los tiene disponibles). HTH
Dibujó el
2

Esto funciona para suprimir "Inicio del búfer" y "Fin del búfer" y no requiere 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))))

Inspirado en https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html pero usa "defadvice" para mayor compatibilidad.

trogdoro
fuente