Emacs - 禁用一些 Minibuffer 消息

Emacs - 禁用一些 Minibuffer 消息

在 Emacs 中,在某些情况下我想阻止消息出现在迷你缓冲区中,主要涉及“缓冲区的开始/结束”和“文本是只读的”。

有什么方法可以阻止这些消息出现在迷你缓冲区中?

另外,有什么重要的原因让我可能不想禁用这些功能吗?从表面上看,我可以轻松地在模式行上查看行号和缓冲区写入状态。

答案1

在 Emacs 25 中,你可以通过绑定inhibit-message到非零值来抑制迷你缓冲区消息:

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

答案2

在 Emacs 25 以及可能的一些早期版本中,最干净的方法如下:

首先定义:

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

然后,如果您想抑制您生成的所有消息,some-function请执行以下操作:

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

例如,我通过以下方式抑制函数ispell-kill-ispell(在ispell.el.gz)产生的消息“Ispell process died”:

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

如果您需要重新启用消息,请运行:

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

需要注意以下几点:

1) 生成的所有消息都some-function将被抑制,函数调用的任何 LISP 函数生成的所有消息也将被抑制。

2) C 代码生成的消息不会被抑制,但这可能就是最好的。

3)您需要确保-*- lexical-binding: t -*-包含在.el文件的第一行。

但是如何找出调用了哪个函数呢message?您可以像其他人建议的那样通过代码进行 grep,但让 Emacs 为您完成这项工作更简单。

如果你定义:

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

然后执行以下操作:

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

您将获得添加到消息中的回溯。从中您可以轻松看到消息的生成位置。

您可以使用以下方法扭转这种情况:

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

另一种方法是建议该message函数并测试是否要打印该消息。如果相关消息是固定字符串,则这很简单。例如,要抑制“Ispell 进程被终止”,您可以定义:

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

然后执行以下操作:

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

如果消息很复杂,这种方法很快就会变得非常混乱。

答案3

你可以有点从 Lisp 代码中执行此操作。为什么“有点”?因为 MESSAGE 是一个用 C 定义的原语,而不是 Lisp 函数,并且,根据 Emacs Lisp 参考手册,来自 C 代码的对原语的调用忽略建议。

因此,为了真正正确地实现您想要的功能,您需要将 MESSAGE 原语重新定义为 Lisp 函数;完成此操作后,您可以使用代码通知它,该代码获取字符串 MESSAGE 将回显到迷你缓冲区,将其与您不想看到的消息列表进行比较,然后根据结果调用或不调用 MESSAGE。理论上,这可以通过例如来实现(defvar *message-prim* (symbol-function 'message)),然后(defun message (format &rest args) ... (funcall *message-prim* format args))——但给定原始参数的 SYMBOL-FUNCTION 返回实际上不可调用的内容,因此 FUNCALL 发出 VOID-FUNCTION 条件信号。

但是,即使这种方法可行,也仍然无法真正解决问题,因为重新定义一个原语只能保证在从 Lisp 代码调用该函数时使用该重新定义;在 C 代码中调用仍可使用原始定义(C 代码可以调用 Emacs Lisp,这种情况下会看到重新定义;当然,C 代码也可以调用 C 代码,这种情况下会看到原始定义。)

我正在考虑修补 C 代码并重新编译 Emacs 以提供适当的消息抑制功能;我并不真正需要该功能,但这可能是一个有趣的练习,特别是因为我不是 C 黑客。与此同时,这是我编写的一些东西,当将其放入文件中、包含在您的一个 init 文件中并根据您的喜好进行自定义时,它将抑制来自 Lisp 代码的消息,这些消息与您列出的抑制字符串完全匹配。只要启用了抑制功能,这些消息就永远不会出现在迷你缓冲区中;您也可以选择是否从缓冲区中抑制它们*Messages*

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

我已经测试过这种方法,它适用于实际由 Lisp 代码生成的消息,例如,当您为 DESCRIBE-FUNCTION 提供一个空字符串参数时,它会发出“您未指定函数”的抱怨。不幸的是,您提到想要抑制的消息(例如“缓冲区开头”、“缓冲区结尾”和“文本为只读”)似乎都来自 C 代码,这意味着您无法通过这种方法抑制它们。

如果我真的有机会得到源代码补丁,它(可能)会反对Emacs 24.3,我将在这个答案中更新有关如何使用它的信息。

答案4

这可以抑制“缓冲区开始”和“缓冲区结束”,并且不需要 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))))

灵感来自https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html但使用“defadvice”来提高兼容性。

相关内容