-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsilence.el
executable file
·201 lines (162 loc) · 7.11 KB
/
silence.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
;;; silence.el --- Silence unwanted messages -*- lexical-binding: t; -*-
;; Copyright (C) 2016 Aaron Harris
;; Author: Aaron Harris <[email protected]>
;; Keywords: convenience
;; Dependencies: `seq', `vizier' (load message suppression only),
;; `validate' (optional)
;; Advised functions from other packages:
;; built-in: `message'
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module provides tools to suppress unwanted messages from
;; appearing in the *Messages* buffer. Note that this is implemented
;; through advice on `message'. This runs contrary to the admonition
;; never to advise a primitive, but this seems to be the only way to
;; accomplish this, and does not seem to cause any unwanted effects.
;; Nevertheless, use at your own risk!
;;
;; Three silencing mechanisms are provided. Each can be used in a
;; different way:
;;
;; First, there is global message suppression. To use this form,
;; require this module, set the variable `silence-enabled-p' to a
;; non-nil value, and populate the list `silence-list'. Members of
;; this list should be either regexps matching the messages to
;; silence, or functions. Functions are called with a single string
;; argument (the message under consideration); if the function returns
;; non-nil, the message will be suppressed.
;;
;; Next, there is the `silence' macro. This macro accepts a list of
;; the same format as `silence-list' and suppresses all matching
;; messages (in addition to those matching the actual contents of
;; `silence-list') for the duration of its body. This can be used to
;; suppress messages within a specific block of code.
;;
;; Finally, there is the macro `silence-loading'. This silences
;; messages generated in its body by the `load' command. Note that
;; this uses an entirely different mechanism than the other two forms
;; of suppression; it temporarily advises the `load' function to
;; supply the argument causing it to suppress its own messages.
;;
;; Both `silence' and `silence-loading' are autoloaded, so there is no
;; need to require this module explicitly unless you are using global
;; message suppression.
;;; Code:
(require 'seq) ; For `seq-some'
(eval-when-compile (require 'dash)) ; For `-lambda'
;;;; User Options
;;===============
(defgroup silence nil
"Silence unwanted messages."
:prefix "silence-"
:link '(emacs-commentary-link "silence")
:group 'convenience)
(defcustom silence-enabled-p nil
"If non-nil, some messages will be silenced.
To control which messages are silenced, see `silence-list'.
See `silence-advice' for more details."
:type 'boolean)
(defcustom silence-list nil
"A list identifying messages to be silenced.
Valid entries are strings and functions.
A string entry is interpreted as a regexp; messages containing a match
will be silenced. Note that the entire message doesn't have to
match.
A function entry is called for each message, with the same args
as the triggering `message' call, and the message is silenced if
the function returns non-nil.
See `silence-advice' for more details."
:type '(repeat (choice string function)))
;;;; General Message Suppression
;;==============================
(defun silence-message-p (msg)
"Return non-nil if MSG should be silenced per `silence-list'.
If `silence-enabled-p' is nil, return nil for any MSG. See
`silence-advice' for more details.
MSG should be a string, already formatted, just as it would
appear in the *Messages* buffer."
(when (require 'validate nil :noerror)
(validate-variable 'silence-list))
(when silence-enabled-p
(seq-some
(lambda (elt)
(cond
((stringp elt) (string-match-p elt msg))
((functionp elt) (funcall elt msg))
:else (error "Invalid entry %s in `silence-list'" elt)))
silence-list)))
(defun silence-advice (fun &rest args)
"Advice to enforce `silence-enabled-p'.
If `silence-enabled-p' is nil, do nothing.
Otherwise, consult `silence-list' to see whether the message
defined by ARGS should be silenced (where ARGS is the arglist for
the pending `message' command.
If a message is silenced, `format' is called instead of
`message'. Thus, the return value is unchanged, but no message
is shown in the *Messages* buffer.
This function is intended as :around advice for `message'. As
`message' is a primitive, this is not ideal, but there doesn't
seem to be any way to achieve this kind of message suppression
otherwise. Because `message' is a primitive, not all messages
can be silenced; calls from C code may avoid being silenced."
(let ((msg (and (car args) (apply #'format args))))
(cond
((null msg) (apply fun args))
((equal (car args) "") (apply fun args))
((silence-message-p msg) msg)
(:else (apply fun args)))))
;; Note that, with the default settings, this advice does nothing.
(advice-add 'message :around #'silence-advice)
;;;; Local Message Suppression
;;============================
;;;###autoload
(defmacro silence (msg-list &rest body)
"Execute BODY silencing messages matching MSG-LIST.
Here MSG-LIST is a list of the same format as `silence-list'.
Its elements will be added to `silence-list' for the duration
of BODY, and `silence-enabled' will be treated as non-nil.
This is accomplished by advising `message'. As `message' is a
primitive, not all messages can be silenced; calls from C code
may avoid being silenced."
(declare (debug ((&rest &or stringp function-form) body))
(indent 1))
`(let ((silence-list (append silence-list
(list ,@msg-list)))
(silence-enabled-p t))
,@body))
;;;; Load Message Suppression
;;===========================
;;;###autoload
(defmacro silence-loading (&rest body)
"Execute BODY silencing `load' messages.
Note that the mechanism used is unrelated to that used by the
`silence' macro. Instead, we advise `load'. As `load' is also a
primitive, the same caveat regarding C calls applies."
(declare (debug t)
(indent 0))
(require 'vizier) ; For `vizier-with-advice'
`(vizier-with-advice
((:genname
load
:filter-args
(-lambda ((file noerror nomessage nosuffix must-suffix))
(list file noerror t nosuffix must-suffix))))
,@body))
;;;; Unloading
;;============
(defun silence-unload-function ()
"Undo changes made to support `silence' module.
More specifically, remove advice `silence-advice' from
`message'."
(advice-remove 'message #'silence-advice))
(provide 'silence)
;;; silence.el ends here