Emacs-禁用一些Minibuffer消息


20

在Emacs中,有些情况下我想防止消息出现在迷你缓冲区中,这主要与“缓冲区的开始/结束”和“文本为只读”有关。

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

另外,是否有一些重要原因我可能不想禁用这些功能?从表面上看,我可以轻松地查看modeline上的行号和缓冲区写状态。


2
没有任何理由需要这些消息,不。这些消息存在的原因是,尝试确保每个命令都具有某些可见效果:当无法执行该命令的预期可见效果时,我们发出一条消息,因此您可以确定该命令确实已运行。
Stefan

Answers:


21

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

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

这是否也适用于从C调用的原语?
亚伦·米勒

1
正如C函数message1调用的那样message3,它应该尊重此变量。
杰克逊

对于抑制恼人的mu4e“正在接收邮件...”消息(let ((inhibit-message t)) (message make-progress-reporter))
很有用

1
奇怪的是,这对我在Emacs 26.1上不起作用。知道为什么吗?
Christian Hudon

1
@ChristianHudon我刚刚在Emacs 26.1中进行了测试,并且没有任何init文件,但它在两个地方都对我有用。请注意,它message返回消息字符串,因此在评估代码时可能会看到返回的字符串。如果您在键盘绑定中评估此代码,则不会打印任何消息(“ 消息”缓冲区中除外)。
杰克逊

9

您可以从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 (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)

我已经对此进行了测试,以处理实际上是从Lisp代码生成的消息,例如,当您给它提供空字符串参数时,DESCRIBE-FUNCTION会回显“您未指定函数”的投诉。不幸的是,您提到的要禁止显示的消息,例如“ Beginning of buffer”,“ End of buffer”和“ Text is read-only”,似乎都是源自C代码,这意味着您将无法用这种方法压制它们。

如果我确实了解源代码补丁,那么它(可能)将针对Emacs 24.3,并且我将使用有关如何使用它的信息来更新此答案。


8

在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 killed” :

(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呢?您可以按照其他人的建议遍历代码,但是让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

您显然正在寻求一种方法来有选择地禁止某些消息。答案是,您需要重新定义或建议发出那些特定消息的代码。

要阻止所有消息(例如在某些代码持续时间内),可以使用fletcl-flet将函数message本地重新定义为(function)ignore。或者使用中所使用的技术edt-electric-helpify:保存的原始定义messagefsetignore,重新fset回原来的高清(虽然它是更好地使用unwind-protect,如果你这样做)。


抱歉,但是您能为我提供如何搜索这些错误消息的帮助吗?在这一点上,几乎感觉到禁用消息比保留消息更麻烦。
bitflips 2013年

1
要搜索“这些错误消息”,请使用grepADired。在Emacs Lisp源文件中搜索错误消息文本(如果可用,还可以在Emacs C文件中搜索)。HTH。
提请

2

这适用于取消“缓冲区开始”和“缓冲区结束”,并且不需要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”以获得更多兼容性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.