17

В Emacs есть некоторые случаи, когда я хотел бы запретить появление сообщений в минибуфере, в основном относящихся к «Beginning /End of buffer» и "Text только для чтения".

Можно ли как-то предотвратить появление этих сообщений в минибуфере?

Кроме того, есть ли какая-то существенная причина, по которой я не хочу отключать их? По номинальной стоимости я так же легко могу посмотреть на номер строки и состояние записи в буфер на моделине.

5 ответов5

18

В Emacs 25 вы можете подавить сообщения минибуфера, inhibit-message об запрете со значением, отличным от nil:

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

Вы можете сортировать это сделать из 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.

Однако, даже если бы это сработало, это все равно не сработало бы, потому что переопределение примитива только гарантирует, что переопределение будет использовано при вызове функции из кода на Лиспе; вызовы в C-коде могут все еще использовать определение примитива. (Возможно, что C-код может вызывать Emacs Lisp, и в таких случаях будет переопределение; также, конечно, для C-кода можно вызывать C-код, и такие случаи будут видеть исходное определение.)

Я смутно обдумываю исправление кода C и перекомпиляцию Emacs для обеспечения надлежащей функциональности подавления сообщений; Мне действительно не нужна эта функциональность, но это может оказаться интересным упражнением, тем более что я не хакер Си. Тем временем, я кое-что написал, что, будучи помещенным в файл, включенный в один из ваших файлов инициализации и настроенный на ваш вкус, будет подавлять сообщения, исходящие из кода 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)

Я проверил это, чтобы работать с сообщениями, которые на самом деле генерируются из кода на Лиспе, например, жалоба "Вы не указали функцию", которую выдает DESCRIBE-FUNCTION, когда вы даете ему пустой строковый аргумент. К сожалению, сообщения, о которых вы упомянули, что хотите подавить, такие как "Начало буфера", "Конец буфера" и "Текст только для чтения", выглядят как все происходящие из кода C, что означает, что вы не сможете подавить их этим методом.

Если я когда-нибудь доберусь до исходного патча, он будет (вероятно) против Emacs 24.3, и я обновлю этот ответ информацией о том, как его использовать.

6

В 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 убит", созданное функцией ispell-kill-ispellispell.el.gz), записав:

(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

Вы, очевидно, просите способ избирательного запрета определенных сообщений. Ответ заключается в том, что вам нужно будет переопределить или сообщить код, который выдает эти конкретные сообщения.

Чтобы запретить все сообщения, например, для некоторого кода, вы можете использовать flet или cl-flet чтобы переопределить функциональное message локально, чтобы (функция) ignore . Или используйте технику, использованную в edt-electric-helpify: сохраните исходное определение message , fset чтобы ignore , fset его обратно к исходному def (хотя лучше, если вы это сделаете, использовать unwind-protect ).

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" для большей совместимости.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .