Emacs - ปิดการใช้งานข้อความ Minibuffer บางส่วน


20

ใน Emacs มีบางกรณีที่ฉันต้องการป้องกันข้อความไม่ให้ปรากฏใน minibuffer ส่วนใหญ่เกี่ยวข้องกับ "การเริ่มต้น / สิ้นสุดการบัฟเฟอร์" และ "ข้อความเป็นแบบอ่านอย่างเดียว"

มีวิธีใดที่ฉันสามารถป้องกันไม่ให้ข้อความเหล่านี้ปรากฏใน minibuffer หรือไม่

นอกจากนี้มีเหตุผลสำคัญที่ฉันอาจไม่ต้องการปิดใช้งานสิ่งเหล่านี้หรือไม่ ที่ใบหน้าฉันสามารถดูหมายเลขแถวและสถานะการเขียนบัฟเฟอร์บน modeline ได้อย่างง่ายดาย


2
ไม่มีเหตุผลว่าทำไมคุณต้องการข้อความเหล่านั้นไม่ เหตุผลที่ข้อความเหล่านั้นมีอยู่คือพยายามตรวจสอบให้แน่ใจว่าทุกคำสั่งมีผลกระทบที่มองเห็นได้: เมื่อไม่สามารถใช้เอฟเฟกต์ที่มองเห็นได้ของคำสั่งเราจึงปล่อยข้อความแทนดังนั้นคุณจึงสามารถบอกได้ว่า
Stefan

คำตอบ:


21

ใน Emacs 25 คุณสามารถระงับข้อความ minibuffer ได้โดยผูกinhibit-messageกับค่าที่ไม่เป็นศูนย์:

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

ใช้งานได้กับการเรียกดั้งเดิมจาก C หรือไม่
Aaron Miller

1
มันควรจะเป็นฟังก์ชั่น C ซึ่งmessage1เรียกmessage3ว่าตัวแปรนี้
Jackson

มีประโยชน์สำหรับการกำจัด mu4e ที่น่ารำคาญ "Retreiving mail ... " ข้อความ:(let ((inhibit-message t)) (message make-progress-reporter))
manandearth

1
สิ่งนี้ไม่ได้ผลสำหรับฉันใน Emacs 26.1 แปลกพอ มีความคิดอะไรไหม
Christian Hudon

1
@ChristianHudon ฉันเพิ่งทดสอบใน Emacs 26.1 และมาสเตอร์ที่ไม่มีไฟล์ init และมันใช้ได้กับฉันทั้งสองแห่ง โปรดทราบว่าmessageส่งคืนสตริงข้อความดังนั้นบางทีคุณอาจเห็นสตริงที่ส่งคืนเมื่อประเมินรหัส หากคุณประเมินรหัสนี้ในการผูกกุญแจแล้วจะไม่มีข้อความใดพิมพ์ออกมา (ยกเว้นในบัฟเฟอร์ข้อความ )
แจ็กสัน

9

คุณสามารถเรียงลำดับจาก Lisp code ทำไม "เรียงลำดับ"? เนื่องจาก MESSAGE เป็นแบบดั้งเดิมกำหนดไว้ใน C แทนที่จะเป็นฟังก์ชั่น Lisp และตามคู่มืออ้างอิงของ Emacs Lisp การเรียกใช้คำสั่งพื้นฐานจากรหัส C ไม่ต้องสนใจคำแนะนำ

ดังนั้นเพื่อให้สามารถใช้งานฟังก์ชั่นที่คุณต้องการได้อย่างแท้จริงคุณจะต้องกำหนด MESSAGE ดั้งเดิมเป็นฟังก์ชั่น Lisp เมื่อคุณทำเช่นนั้นคุณสามารถให้คำแนะนำกับรหัสที่ได้รับข้อความ MESSAGE จะสะท้อนไปที่ minibuffer เปรียบเทียบกับรายการข้อความที่คุณไม่ต้องการที่จะเห็นแล้วโทรหรือไม่โทรข้อความขึ้นอยู่กับ บนผลลัพธ์ ในทางทฤษฎีสิ่งนี้สามารถทำได้โดยเช่น(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 ซึ่งตรงกับสตริงที่คุณต้องการ ตราบใดที่มีการเปิดใช้งานการปราบปรามข้อความเหล่านี้จะไม่ปรากฏใน minibuffer คุณมีตัวเลือกว่าจะระงับพวกเขาจาก*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)

ฉันได้ทดสอบสิ่งนี้เพื่อทำงานกับข้อความที่สร้างขึ้นจริงจากรหัส Lisp เช่นการร้องเรียน "คุณไม่ได้ระบุฟังก์ชั่น" ที่สะท้อนโดย 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(ในispell.el.gz) โดยการเขียน:

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

หากคุณต้องการเปิดใช้งานข้อความอีกครั้งให้เรียกใช้:

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

บางสิ่งที่ควรทราบ:

1) ข้อความทั้งหมดที่ผลิตโดยsome-functionจะถูกระงับเช่นเดียวกับข้อความทั้งหมดที่ผลิตโดยฟังก์ชั่นเสียงกระเพื่อมใด ๆ ที่ฟังก์ชั่นการโทร

2) ข้อความที่ผลิตโดยรหัส C จะไม่ถูกบีบอัด แต่นั่นอาจเป็นสิ่งที่ดีที่สุด

3) คุณต้องตรวจสอบให้แน่ใจว่า-*- lexical-binding: t -*-มีอยู่ในบรรทัดแรกของ.elไฟล์ของคุณ

แต่คุณจะทราบได้messageอย่างไรว่าฟังก์ชันใดที่เรียกว่า คุณสามารถพิมพ์รหัสได้ตามที่คนอื่นแนะนำ แต่การปล่อยให้ Emac ทำงานให้คุณได้ง่ายขึ้น

หากคุณกำหนด:

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

คุณจะได้รับ backtrace เพิ่มในข้อความ จากนี้คุณสามารถดูว่าข้อความถูกสร้างขึ้นที่ใด

คุณสามารถย้อนกลับได้ด้วย:

(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ถ้าคุณทำอย่างนั้น)


ขออภัยคุณจะสามารถช่วยฉันในการค้นหาข้อความแสดงข้อผิดพลาดเหล่านี้ได้อย่างไร ณ จุดนี้มันเกือบจะรู้สึกว่ามันเป็นปัญหาในการปิดการใช้งานข้อความมากกว่าที่จะเก็บไว้จากพวกเขา
bitflips

1
ในการค้นหา "ข้อความแสดงข้อผิดพลาดเหล่านี้" ให้ใช้grepหรือเป็นภาษาADired ค้นหาข้อความแสดงข้อผิดพลาดในไฟล์ต้นฉบับ Emacs Lisp (และอาจอยู่ในไฟล์ Emacs C หากคุณมีอยู่) HTH
ดึง

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" เพื่อความเข้ากันได้มากกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.