Emacs - Nonaktifkan Beberapa Pesan Minibuffer

20

Di Emacs, ada beberapa kasus di mana saya ingin mencegah agar pesan tidak muncul di minibuffer, terutama yang berkaitan dengan "Awal / Akhir buffer" dan "Teks hanya-baca".

Apakah ada cara saya mencegah pesan ini muncul di minibuffer?

Juga, apakah ada beberapa alasan penting mengapa saya tidak ingin menonaktifkan ini? Pada nilai nominal, saya dapat dengan mudah melihat nomor baris dan status tulis buffer pada modeline.

mellowmaroon
sumber
2
Tidak ada alasan mengapa Anda membutuhkan pesan itu, tidak. Alasan mengapa pesan-pesan itu ada adalah untuk mencoba dan memastikan bahwa setiap perintah memiliki beberapa efek yang terlihat: ketika efek yang diharapkan dari perintah tersebut tidak dapat dilakukan, kami malah memancarkan sebuah pesan, sehingga Anda dapat mengetahui bahwa perintah itu memang dijalankan.
Stefan

Jawaban:

21

Di Emacs 25, Anda dapat menekan pesan minibuffer dengan mengikat inhibit-messageke nilai nihil:

(let ((inhibit-message t))
  (message "Listen to me, you!"))
Jackson
sumber
Apakah ini bekerja pada primitif yang dipanggil dari C juga?
Aaron Miller
1
Seharusnya, seperti message1panggilan fungsi C message3, yang menghormati variabel ini.
Jackson
Berguna untuk menekan pesan "Retreiving mail ..." mu4e yang menjengkelkan:(let ((inhibit-message t)) (message make-progress-reporter))
manandearth
1
Ini tidak bekerja untuk saya di Emacs 26.1, anehnya. Ada yang tahu kenapa?
Christian Hudon
1
@ChristianHudon Saya baru saja menguji Emacs 26.1 dan master tanpa file init, dan itu berfungsi untuk saya di kedua tempat. Catatan yang messagemengembalikan string pesan, jadi mungkin Anda melihat string yang dikembalikan saat mengevaluasi kode. Jika Anda mengevaluasi kode ini dalam pengikatan tombol, maka tidak ada pesan yang akan dicetak (kecuali di buffer Pesan ).
Jackson
9

Anda dapat semacam melakukan ini dari kode Lisp. Kenapa "semacam"? Karena MESSAGE adalah primitif, didefinisikan dalam C, alih-alih fungsi Lisp, dan, sesuai manual referensi Emacs Lisp , panggilan ke primitif dari kode C mengabaikan saran.

Oleh karena itu, untuk benar-benar melakukan pekerjaan yang tepat dalam mengimplementasikan fungsionalitas yang Anda inginkan, Anda perlu mendefinisikan kembali primitif MESSAGE sebagai fungsi Lisp; setelah Anda selesai melakukannya, Anda kemudian dapat menyarankannya dengan kode yang memperoleh string MESSAGE akan bergema ke minibuffer, membandingkannya dengan daftar pesan yang tidak ingin Anda lihat, dan kemudian menelepon atau tidak memanggil MESSAGE tergantung pada hasilnya. Secara teori, ini bisa dicapai dengan misalnya (defvar *message-prim* (symbol-function 'message)), dan kemudian (defun message (format &rest args) ... (funcall *message-prim* format args))- tetapi SIMBOL-FUNGSI memberikan argumen primitif mengembalikan sesuatu yang sebenarnya tidak bisa dipanggil, sehingga FUNCALL memberi sinyal kondisi VOID-FUNGSI.

Namun, bahkan jika itu berhasil, itu masih tidak benar-benar melakukan trik, karena mendefinisikan ulang primitif hanya menjamin bahwa redefinisi akan digunakan ketika fungsi dipanggil dari kode Lisp; panggilan dalam kode C masih dapat menggunakan definisi primitif . (Kode C memungkinkan untuk memanggil Emacs Lisp, dan kasus-kasus seperti itu akan melihat redefinisi; tentu saja kode C juga dapat memanggil kode C, dan kasus-kasus seperti itu akan melihat definisi asli.)

Saya agak merenungkan menambal kode C dan mengkompilasi ulang Emacs untuk menyediakan fungsionalitas penindasan pesan yang tepat; Saya tidak benar-benar membutuhkan fungsionalitas itu, tetapi mungkin membuktikan latihan yang menarik, terutama karena saya bukan seorang hacker C. Sementara itu, inilah sesuatu yang saya siapkan, yang ketika dimasukkan ke dalam file, termasuk dari salah satu file init Anda, dan disesuaikan dengan selera Anda, akan menekan pesan yang berasal dari kode Lisp yang persis sama dengan string yang Anda cantumkan untuk penindasan. Selama penindasan diaktifkan, pesan-pesan ini tidak akan pernah muncul di minibuffer; Anda memiliki opsi apakah akan menekan mereka dari *Messages*buffer juga.

;; 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)

Saya telah menguji ini untuk bekerja dengan pesan yang sebenarnya dihasilkan dari kode Lisp, misalnya keluhan "Anda tidak menentukan fungsi" yang digaungkan oleh DESCRIBE-FUNCTION saat Anda memberikan argumen string kosong. Sayangnya, pesan yang Anda sebut ingin ditekan, seperti "Awal buffer", "End buffer", dan "Teks hanya baca", tampaknya semua berasal dari kode C, yang berarti Anda tidak akan dapat menekan mereka dengan metode ini.

Jika saya pernah berkeliling ke patch sumber, itu (mungkin) akan menentang Emacs 24.3 , dan saya akan memperbarui jawaban ini dengan informasi tentang bagaimana cara menggunakannya.

Aaron Miller
sumber
8

Di Emacs 25 dan mungkin di beberapa versi sebelumnya, cara paling bersih untuk melakukan ini adalah sebagai berikut:

Definisi pertama:

(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))))

Kemudian jika Anda ingin menekan semua pesan yang some-functionAnda lakukan:

(advice-add 'some-function :around #'suppress-messages)

Misalnya, saya menekan pesan "Proses Ispell terbunuh" yang dihasilkan oleh fungsi ispell-kill-ispell(dalam ispell.el.gz) dengan menulis:

(advice-add 'ispell-kill-ispell :around #'suppress-messages)

Jika Anda perlu mengaktifkan kembali pesan, jalankan:

(advice-remove 'some-function #'suppress-messages)

Beberapa hal yang perlu diperhatikan:

1) Semua pesan yang dihasilkan oleh some-functionakan ditekan seperti halnya semua pesan yang diproduksi oleh fungsi panggil fungsi panggilan.

2) Pesan yang dihasilkan oleh kode C tidak akan ditekan tetapi itu mungkin yang terbaik.

3) Anda harus memastikan -*- lexical-binding: t -*-terkandung di baris pertama .elfile Anda .

Tetapi bagaimana Anda mengetahui fungsi mana yang dipanggil message? Anda dapat membaca kode seperti yang disarankan orang lain, tetapi lebih mudah membiarkan Emacs melakukan pekerjaan untuk Anda.

Jika Anda mendefinisikan:

(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))))

dan kemudian lakukan:

(advice-add 'message :around #'who-called-me?)

Anda akan mendapatkan jejak balik ditambahkan ke pesan. Dari sini Anda dapat dengan mudah melihat di mana pesan itu dihasilkan.

Anda dapat membalikkan ini dengan:

(advice-remove 'message #'who-called-me?)

Pendekatan alternatif adalah memberi saran messagefungsi dan uji untuk melihat apakah Anda ingin mencetak pesan atau tidak. Ini sederhana jika pesan yang dimaksud adalah string tetap. Misalnya untuk menekan "proses Ispell terbunuh" Anda dapat mendefinisikan:

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

dan kemudian lakukan:

(advice-add 'message :around #'suppress-ispell-message)

Pendekatan ini segera menjadi sangat berantakan jika pesannya rumit.

Bernard Hurley
sumber
3

Anda tampaknya meminta cara untuk secara selektif menghambat pesan tertentu . Jawabannya adalah Anda perlu mendefinisikan ulang atau menyarankan kode yang mengeluarkan pesan - pesan khusus itu .

Untuk mencegah semua pesan, misalnya selama durasi beberapa kode, Anda dapat menggunakan fletatau cl-fletuntuk mendefinisikan kembali fungsi messagesecara lokal ke (fungsi) ignore. Atau gunakan teknik yang digunakan dalam edt-electric-helpify: simpan definisi asli message, fsetuntuk ignore, fsetmengembalikannya ke def asli (meskipun lebih baik digunakan unwind-protectjika Anda melakukannya).

Drew
sumber
Maaf, tetapi apakah Anda dapat membantu saya tentang bagaimana saya dapat mencari pesan kesalahan ini? Pada titik ini, rasanya lebih sulit menonaktifkan pesan daripada menyimpannya.
mellowmaroon
1
Untuk mencari "pesan-pesan kesalahan ini", gunakan grepatau Adalam Dired. Cari teks pesan kesalahan dalam file sumber Emacs Lisp (dan mungkin juga dalam file Emacs C, jika Anda memilikinya). HTH.
Drew
2

Ini berfungsi untuk menekan "Permulaan buffer" dan "Akhir buffer" dan tidak memerlukan 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))))

Terinspirasi oleh https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html tetapi menggunakan "defadvice" untuk kompatibilitas yang lebih baik.

trogdoro
sumber