Emacs - 禁用一些 Minibuffer 消息

22 emacs

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

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

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

Jac*_*son 22

在 Emacs 25 中,您可以通过绑定inhibit-message到非 nil 值来抑制 minibuffer 消息:

(let ((inhibit-message t))
  (message "Listen to me, you!"))
Run Code Online (Sandbox Code Playgroud)


Ber*_*ley 9

在 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))))
Run Code Online (Sandbox Code Playgroud)

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

(advice-add 'some-function :around #'suppress-messages)
Run Code Online (Sandbox Code Playgroud)

例如,我通过编写以下命令来抑制由函数ispell-kill-ispell(in ispell.el.gz)产生的消息“Ispell 进程被杀死” :

(advice-add 'ispell-kill-ispell :around #'suppress-messages)
Run Code Online (Sandbox Code Playgroud)

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

(advice-remove 'some-function #'suppress-messages)
Run Code Online (Sandbox Code Playgroud)

需要注意的几点:

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))))
Run Code Online (Sandbox Code Playgroud)

然后做:

(advice-add 'message :around #'who-called-me?)
Run Code Online (Sandbox Code Playgroud)

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

您可以通过以下方式扭转这一点:

(advice-remove 'message #'who-called-me?)
Run Code Online (Sandbox Code Playgroud)

另一种方法是建议message函数并测试是否要打印消息。如果所讨论的消息是固定字符串,这很简单。例如,要抑制“Ispell 进程被杀死”,您可以定义:

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

然后做:

(advice-add 'message :around #'suppress-ispell-message)
Run Code Online (Sandbox Code Playgroud)

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


Aar*_*ler 8

你可以从 Lisp 代码做到这一点。为什么是“某种”?因为 MESSAGE 是一个原语,在 C 中定义,而不是 Lisp 函数,并且根据 Emacs Lisp 参考手册,从 C 代码调用原语会忽略建议。

因此,为了真正正确地实现您想要的功能,您需要将 MESSAGE 原语重新定义为 Lisp 函数;完成此操作后,您可以使用获取字符串 MESSAGE 的代码通知它会回显到迷你缓冲区,将其与您不想看到的消息列表进行比较,然后调用或不调用 MESSAGE 取决于结果上。理论上,这可以通过 eg 来完成(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)
Run Code Online (Sandbox Code Playgroud)

我已经测试过它可以处理实际从 Lisp 代码生成的消息,例如,当你给它一个空字符串参数时,DESCRIBE-FUNCTION 回应的“你没有指定函数”抱怨。不幸的是,您提到要抑制的消息,例如“缓冲区开始”、“缓冲区结束”和“文本是只读的”,似乎都源自 C 代码,这意味着您将无法用这种方法压制它们。

如果我真的解决了源补丁,它(可能)会针对Emacs 24.3,我将使用有关如何使用它的信息更新此答案。