Emacs - отключить некоторые сообщения минибуфера

20

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

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

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

bitflips
источник
2
Нет никаких причин, почему вам нужны эти сообщения, нет. Причина, по которой эти сообщения существуют, состоит в том, чтобы попытаться убедиться, что каждая команда имеет какой-то видимый эффект: когда ожидаемый видимый эффект команды не может быть выполнен, мы вместо этого отправляем сообщение, так что вы можете сказать, что команда действительно была выполнена.
Стефан,

Ответы:

21

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

(let ((inhibit-message t))
  (message "Listen to me, you!"))
Джексон
источник
Это работает и для примитивов, вызываемых из C?
Аарон Миллер
1
Он должен, как message1вызывает функция C message3, уважать эту переменную.
Джексон,
Полезно для подавления раздражающего сообщения mu4e «Получение почты ...»:(let ((inhibit-message t)) (message make-progress-reporter))
manandearth
1
Это не работает для меня на Emacs 26.1, как ни странно. Есть идеи почему?
Кристиан Худон
1
@ChristianHudon Я только что протестировал в Emacs 26.1 и мастер без файла инициализации, и это работает для меня в обоих местах. Обратите внимание, что 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.

Однако, даже если бы это сработало, это все равно не сработало бы, потому что переопределение примитива только гарантирует, что переопределение будет использовано при вызове функции из кода на Лиспе; вызовы в 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 , и я обновлю этот ответ информацией о том, как его использовать.

Аарон Миллер
источник
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 убит», созданный функцией ispell-kill-ispell(in ispell.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вернуться к исходному определению (хотя лучше использовать, unwind-protectесли вы это сделаете).

Нарисовалась
источник
Извините, но не могли бы вы помочь мне с поиском этих сообщений об ошибках? В этот момент почти кажется, что отключить сообщения гораздо сложнее, чем сохранить их.
bitflips
1
Для поиска «этих сообщений об ошибках» используйте grepили Aв Dired. Найдите текст сообщения об ошибке в исходных файлах Emacs Lisp (и, возможно, также в файлах Emacs C, если они у вас есть). НТН.
Дрю
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» для большей совместимости.

trogdoro
источник