Emacs - Desativar algumas mensagens do Minibuffer

20

No Emacs, há alguns casos em que eu gostaria de impedir a exibição de mensagens no minibuffer, principalmente relacionadas a "Início / Fim do buffer" e "O texto é somente leitura".

Existe alguma maneira de impedir que essas mensagens apareçam no minibuffer?

Além disso, há algum motivo significativo para não querer desativá-los? No valor nominal, posso olhar com a mesma facilidade o número da linha e o status de gravação do buffer na modelagem.

bitflips
fonte
2
Não há razão para você precisar dessas mensagens, não. A razão pela qual essas mensagens existem é tentar garantir que todo comando tenha algum efeito visível: quando o efeito visível esperado do comando não puder ser executado, em vez disso, emitimos uma mensagem, para que você possa dizer que o comando foi realmente executado.
Stefan

Respostas:

21

No Emacs 25, você pode suprimir as mensagens do minibuffer vinculando-se inhibit-messagea um valor não nulo:

(let ((inhibit-message t))
  (message "Listen to me, you!"))
Jackson
fonte
Isso funciona com primitivas chamadas de C também?
Aaron Miller
1
Deveria, como message1chama a função C message3, que respeite essa variável.
Jackson
Útil para suprimindo o mu4e irritante "Retreiving mail ..." mensagem:(let ((inhibit-message t)) (message make-progress-reporter))
manandearth
1
Isso não funciona para mim no Emacs 26.1, por incrível que pareça. Alguma idéia do porquê?
Christian Hudon 04/04
1
@ChristianHudon Acabei de testar o Emacs 26.1 e o master sem nenhum arquivo init, e funciona para mim nos dois lugares. Observe que messageretorna a sequência de mensagens, então talvez você esteja vendo a sequência retornada ao avaliar o código. Se você avaliar esse código em uma combinação de teclas, nenhuma mensagem será impressa (exceto no buffer de Mensagens ).
Jackson
9

Você pode tipo de fazer isso a partir do código Lisp. Por que "mais ou menos"? Como MESSAGE é uma primitiva, definida em C, em vez de uma função Lisp, e, de acordo com o manual de referência do Emacs Lisp , as chamadas para primitivas do código C ignoram os conselhos.

Portanto, para realmente executar um trabalho adequado de implementação da funcionalidade desejada, você precisará redefinir a primitiva MESSAGE como uma função Lisp; depois de fazer isso, você poderá aconselhá-lo com o código que obtém a sequência que MESSAGE ecoaria no minibuffer, comparando-o com uma lista de mensagens que você não deseja ver e, em seguida, chama ou não chama MESSAGE, dependendo no resultado. Em teoria, isso poderia ser realizado por exemplo (defvar *message-prim* (symbol-function 'message)), e então (defun message (format &rest args) ... (funcall *message-prim* format args))- mas SYMBOL-FUNCTION, dado um argumento primitivo, retorna algo que não é realmente exigível, então o FUNCALL sinaliza uma condição de VOID-FUNCTION.

No entanto, mesmo se isso funcionasse, ainda não funcionaria, porque a redefinição de uma primitiva apenas garante que a redefinição será usada quando a função for chamada a partir do código Lisp; chamadas no código C ainda podem usar a definição primitiva . (É possível que o código C chame o Emacs Lisp, e esses casos terão a redefinição; também é possível que o código C chame o código C, e esses casos terão a definição original.)

Estou vagamente pensando em corrigir o código C e recompilando o Emacs para fornecer a funcionalidade adequada de supressão de mensagens; Eu realmente não preciso dessa funcionalidade, mas pode ser um exercício interessante, especialmente porque eu não sou um hacker C. Enquanto isso, aqui está algo que eu criei que, quando solto em um arquivo, incluído em um de seus arquivos init e personalizado ao seu gosto, suprimirá as mensagens originárias do código Lisp que correspondem exatamente às strings que você lista para supressão. Enquanto a supressão estiver ativada, essas mensagens nunca aparecerão no minibuffer; você tem a opção de suprimi-los também do *Messages*buffer.

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

Eu testei isso para trabalhar com mensagens que são realmente geradas a partir do código Lisp, por exemplo, a reclamação "Você não especificou uma função" ecoada por DESCRIBE-FUNCTION quando você fornece um argumento de string vazio. Infelizmente, as mensagens que você menciona que desejam suprimir, como "Início do buffer", "Fim do buffer" e "O texto é somente leitura", parecem todas originárias do código C, o que significa que você não poderá suprima-os por este método.

Se algum dia eu chegar ao patch de origem (provavelmente) será contra o Emacs 24.3 , e atualizarei esta resposta com informações sobre como usá-lo.

Aaron Miller
fonte
8

No Emacs 25 e provavelmente em algumas versões anteriores, a maneira mais limpa de fazer isso é a seguinte:

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

Então, se você deseja suprimir todas as mensagens produzidas por some-functionvocê:

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

Por exemplo, suprimo a mensagem "Processo Ispell morto" produzido pela função ispell-kill-ispell(in ispell.el.gz) escrevendo:

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

Se você precisar reativar as mensagens, execute:

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

Algumas coisas a serem observadas:

1) Todas as mensagens produzidas por some-functionserão suprimidas, assim como todas as mensagens produzidas por qualquer função lisp que a função chamar.

2) As mensagens produzidas pelo código C não serão suprimidas, mas provavelmente é tudo o melhor.

3) Você precisa ter certeza de que -*- lexical-binding: t -*-está contido na primeira linha do seu .elarquivo.

Mas como você descobre qual função é chamada message? Você pode verificar o código como alguém sugeriu, mas é mais fácil deixar o Emacs fazer o trabalho por você.

Se você definir:

(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 então faça:

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

você receberá um backtrace adicionado à mensagem. A partir disso, você pode ver facilmente onde a mensagem foi gerada.

Você pode reverter isso com:

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

Uma abordagem alternativa seria aconselhar a messagefunção e testar se você deseja imprimir a mensagem ou não. Isso é simples se a mensagem em questão for uma sequência fixa. Por exemplo, para suprimir o "processo Ispell morto", você pode definir:

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

e então faça:

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

Essa abordagem logo fica muito confusa se a mensagem for complicada.

Bernard Hurley
fonte
3

Aparentemente, você está pedindo uma maneira de inibir seletivamente certas mensagens. A resposta para isso é que você precisaria redefinir ou aconselhar o código que emite essas mensagens específicas .

Para impedir todas as mensagens, por exemplo, durante o período de algum código, você pode usar fletou cl-fletredefinir a função messagelocalmente para (função) ignore. Ou use a técnica usada em edt-electric-helpify: salve a definição original de message, fsetpara ignoreretorná- fsetla ao padrão original (embora seja melhor usar unwind-protectse você fizer isso).

Desenhou
fonte
Desculpe, mas você poderia me ajudar em como posso procurar essas mensagens de erro? Neste ponto, parece quase mais desabilitar as mensagens do que mantê-las.
bitflips
1
Para procurar "essas mensagens de erro", use grepou Aem Dired. Procure o texto da mensagem de erro nos arquivos de origem do Emacs Lisp (e possivelmente também nos arquivos do Emacs C, se você os tiver disponíveis). HTH.
Tirou
2

Isso funciona para suprimir "Início do buffer" e "Fim do buffer" e não requer o 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 em https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html, mas usa "defadvice" para obter mais compatibilidade.

trogdoro
fonte