在 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”来提高兼容性。