Emacs - Vô hiệu hóa một số tin nhắn Minibuffer


20

Trong Emacs, có một số trường hợp tôi muốn ngăn các tin nhắn xuất hiện trong bộ thu nhỏ, chủ yếu liên quan đến "Bắt đầu / Kết thúc bộ đệm" và "Văn bản chỉ đọc".

Có cách nào tôi có thể ngăn những tin nhắn này xuất hiện trong xe buýt nhỏ không?

Ngoài ra, có một số lý do quan trọng tôi có thể không muốn vô hiệu hóa những thứ này? Theo mệnh giá, tôi có thể dễ dàng nhìn vào số hàng và trạng thái ghi bộ đệm trên modeline.


2
Không có lý do tại sao bạn cần những tin nhắn đó, không. Lý do tại sao các thông báo đó tồn tại là để thử và đảm bảo rằng mọi lệnh đều có hiệu ứng rõ ràng: khi hiệu ứng hiển thị mong đợi của lệnh không thể được thực hiện, thay vào đó chúng tôi phát ra một thông báo, vì vậy bạn có thể nói rằng lệnh thực sự đã chạy.
Stefan

Câu trả lời:


21

Trong Emacs 25, bạn có thể chặn các tin nhắn của bộ thu nhỏ bằng cách liên kết inhibit-messagevới giá trị không phải là số không:

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

Điều này có hoạt động trên các nguyên thủy được gọi là từ C không?
Aaron Miller

1
Nó nên, như hàm C message1gọi message3, tôn trọng biến này.
Jackson

Hữu ích cho việc nén tin nhắn "Trả lời thư ..." của (let ((inhibit-message t)) (message make-progress-reporter))
mu4e

1
Điều này không hiệu quả với tôi trên Emacs 26.1, thật kỳ lạ. Bất cứ ý tưởng tại sao?
Christian Hudon

1
@ChristianHudon Tôi mới thử nghiệm trong Emacs 26.1 và master không có tệp init, và nó hoạt động với tôi ở cả hai nơi. Lưu ý rằng messagetrả về chuỗi thông báo, vì vậy có thể bạn đang thấy chuỗi được trả về khi đánh giá mã. Nếu bạn đánh giá mã này trong một liên kết phím, thì sẽ không có thông báo nào được in (ngoại trừ trong bộ đệm Tin nhắn ).
Jackson

9

Bạn có thể sắp xếp làm điều này từ mã Lisp. Tại sao "sắp xếp"? Bởi vì MESSAGE là một nguyên thủy, được định nghĩa trong C, thay vì hàm Lisp và, theo hướng dẫn tham khảo Emacs Lisp , gọi đến các nguyên thủy từ mã C bỏ qua lời khuyên.

Do đó, để thực sự thực hiện đúng công việc thực hiện chức năng mà bạn mong muốn, bạn cần xác định lại nguyên hàm MESSAGE dưới dạng hàm Lisp; Sau khi thực hiện xong, bạn có thể tư vấn cho nó bằng mã nhận được chuỗi MESSAGE sẽ lặp lại với bộ thu nhỏ, so sánh nó với một danh sách các tin nhắn bạn không muốn xem, sau đó gọi hoặc không gọi MESSAGE tùy thuộc về kết quả. Về lý thuyết, điều này có thể được thực hiện bằng ví dụ (defvar *message-prim* (symbol-function 'message)), và sau đó (defun message (format &rest args) ... (funcall *message-prim* format args))- nhưng SYMBOL-FUNCTION đưa ra một đối số nguyên thủy trả về một cái gì đó không thực sự có thể gọi được, do đó FUNCALL báo hiệu điều kiện VOID-FUNCTION.

Tuy nhiên, ngay cả khi điều đó có hiệu quả, nó vẫn không thực sự tạo ra mánh khóe, bởi vì việc xác định lại một nguyên thủy chỉ đảm bảo rằng định nghĩa lại sẽ được sử dụng khi hàm được gọi từ mã Lisp; các cuộc gọi trong mã C vẫn có thể sử dụng định nghĩa nguyên thủy . (Mã C có thể gọi vào Emacs Lisp và các trường hợp như vậy sẽ thấy định nghĩa lại, tất nhiên, mã C cũng có thể gọi mã C và các trường hợp như vậy sẽ thấy định nghĩa ban đầu.)

Tôi đang mơ hồ vá lỗi mã C và biên dịch lại Emacs để cung cấp chức năng triệt tiêu thông điệp phù hợp; Tôi không thực sự cần chức năng đó, nhưng nó có thể chứng minh một bài tập thú vị, đặc biệt vì tôi không phải là hacker C. Trong khi đó, đây là thứ tôi đã đánh cắp, khi được thả vào một tệp, được bao gồm từ một trong các tệp init của bạn và được tùy chỉnh theo sở thích của bạn, sẽ loại bỏ các tin nhắn có nguồn gốc từ mã Lisp khớp chính xác với chuỗi bạn liệt kê để loại bỏ. Chừng nào việc kích hoạt được kích hoạt, những thông báo này sẽ không bao giờ xuất hiện trong xe buýt nhỏ; bạn có tùy chọn xem có nên loại bỏ chúng khỏi *Messages*bộ đệm hay không.

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

Tôi đã thử nghiệm tính năng này để hoạt động với các thông báo thực sự được tạo từ mã Lisp, ví dụ: "Bạn không chỉ định hàm" được lặp lại bởi MÔ TẢ CHỨC NĂNG khi bạn đưa ra một đối số chuỗi trống. Thật không may, các thông báo bạn đề cập muốn loại bỏ, chẳng hạn như "Bắt đầu bộ đệm", "Kết thúc bộ đệm" và "Văn bản chỉ đọc", xuất hiện tất cả để bắt nguồn từ mã C, có nghĩa là bạn sẽ không thể triệt tiêu chúng bằng phương pháp này.

Nếu tôi từng tìm hiểu về bản vá nguồn, nó sẽ (có thể) chống lại Emacs 24.3 và tôi sẽ cập nhật câu trả lời này với thông tin về cách sử dụng nó.


8

Trong Emacs 25 và có lẽ trong một số phiên bản trước đó, cách làm sạch nhất như sau:

Xác định đầu tiên:

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

Sau đó, nếu bạn muốn loại bỏ tất cả các tin nhắn do some-functionbạn tạo ra :

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

Chẳng hạn, tôi loại bỏ thông báo "Quá trình Ispell bị giết" được tạo bởi hàm ispell-kill-ispell(in ispell.el.gz) bằng cách viết:

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

Nếu bạn cần kích hoạt lại các tin nhắn thì hãy chạy:

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

Một số điều cần lưu ý:

1) Tất cả các tin nhắn được tạo bởi some-functionsẽ bị loại bỏ vì tất cả các tin nhắn được tạo bởi bất kỳ chức năng lisp nào mà hàm gọi.

2) Tin nhắn được tạo bởi mã C sẽ không bị nén nhưng đó có lẽ là điều tốt nhất.

3) Bạn cần đảm bảo -*- lexical-binding: t -*-được chứa trong dòng đầu tiên của .eltệp.

Nhưng làm thế nào để bạn tìm ra chức năng được gọi là message? Bạn có thể grep qua mã như người khác đề xuất, nhưng để Emacs làm việc cho bạn dễ dàng hơn.

Nếu bạn xác định:

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

và sau đó làm:

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

bạn sẽ nhận được một backtrace được thêm vào tin nhắn. Từ đây bạn có thể dễ dàng thấy nơi tin nhắn được tạo ra.

Bạn có thể đảo ngược điều này với:

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

Một cách tiếp cận khác là tư vấn messagechức năng và kiểm tra xem bạn có muốn in tin nhắn hay không. Điều này là đơn giản nếu thông điệp trong câu hỏi là một chuỗi cố định. Ví dụ: để chặn "quá trình Ispell bị giết" bạn có thể xác định:

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

và sau đó làm:

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

Cách tiếp cận này sẽ sớm trở nên rất lộn xộn nếu tin nhắn là bất cứ điều gì phức tạp.


3

Bạn rõ ràng đang yêu cầu một cách để chọn lọc ức chế một số tin nhắn nhất định . Câu trả lời cho điều đó là bạn sẽ cần xác định lại hoặc tư vấn mã phát hành các thông báo cụ thể đó .

Để ngăn chặn tất cả các thông báo, ví dụ như thời lượng của một số mã, bạn có thể sử dụng flethoặc cl-fletxác định lại hàm messagecục bộ thành (hàm) ignore. Hoặc sử dụng kỹ thuật được sử dụng trong edt-electric-helpify: lưu định nghĩa ban đầu về message, fsetđể ignore, đưa fsetnó trở lại def gốc (mặc dù sẽ tốt hơn unwind-protectnếu sử dụng nếu bạn làm điều đó).


Xin lỗi, nhưng bạn có thể giúp tôi về cách tôi có thể tìm kiếm các thông báo lỗi này không? Tại thời điểm này, gần như cảm thấy rắc rối hơn khi vô hiệu hóa các tin nhắn hơn là giữ chúng.
bitflips

1
Để tìm kiếm "các thông báo lỗi" này, hãy sử dụng grephoặc Atrong Dired. Tìm kiếm văn bản thông báo lỗi trong các tệp nguồn Emacs Lisp (và có thể trong các tệp Emacs C, nếu bạn có sẵn chúng). HTH.
vẽ

2

Điều này hoạt động để triệt tiêu "Bắt đầu bộ đệm" và "Kết thúc bộ đệm" và không yêu cầu 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))))

Lấy cảm hứng từ https://lists.gnu.org/archive/html/help-gnu-emacs/2015-12/msg00189.html nhưng sử dụng "defadvice" để tương thích hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.