25582
|
1 ;;; whitespace.el --- Warn about and clean bogus whitespaces in the file.
|
|
2
|
|
3 ;; Copyright (C) 1999 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Rajesh Vaidheeswarran <rv@dsmit.com>
|
|
6 ;; Keywords: convenience
|
|
7
|
|
8 ;; This file is part of GNU Emacs.
|
|
9
|
|
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
11 ;; it under the terms of the GNU General Public License as published by
|
|
12 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
13 ;; any later version.
|
|
14
|
|
15 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18 ;; GNU General Public License for more details.
|
|
19
|
|
20 ;; You should have received a copy of the GNU General Public License
|
|
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
23 ;; Boston, MA 02111-1307, USA.
|
|
24
|
|
25 ;;; Commentary:
|
|
26
|
|
27 ;; Whitespace.el URL: http://www.dsmit.com/lisp/
|
|
28
|
|
29 ;; Exported functions:
|
|
30
|
|
31 ;; `whitespace-buffer' - To check the current buffer for whitespace problems.
|
|
32 ;; `whitespace-cleanup' - To cleanup all whitespaces in the current buffer.
|
|
33
|
|
34 ;;; Code:
|
|
35
|
|
36 ;; add a hook to find-file-hooks and kill-buffer-hook
|
|
37 (add-hook 'find-file-hooks 'whitespace-buffer)
|
|
38 (add-hook 'kill-buffer-hook 'whitespace-buffer)
|
|
39
|
|
40 (defvar whitespace-version "1.9" "Version of the whitespace library.")
|
|
41 (defvar whitespace-indent-regexp (concat "^\\( *\\) " " ")
|
|
42 "Any 8 or more spaces that can be replaced with a TAB")
|
|
43 (defvar whitespace-spacetab-regexp " \t" "A TAB followed by a space")
|
|
44 (defvar whitespace-ateol-regexp "[ \t]$" "A TAB or a space at the EOL")
|
|
45 (defvar whitespace-errbuf "*Whitespace Errors*"
|
|
46 "The buffer where the errors will appear")
|
|
47
|
|
48 ;; Find out what type of emacs we are running in.
|
|
49 (defvar whitespace-running-emacs (if (string-match "XEmacs\\|Lucid"
|
|
50 emacs-version) nil t)
|
|
51 "If the current Emacs is not XEmacs, then, this is t.")
|
|
52
|
|
53 ;; For users of Emacs 19.x, defgroup and defcustom are not defined.
|
|
54
|
|
55 (if (< (string-to-int emacs-version) 20)
|
|
56 (progn
|
|
57 (defmacro defgroup (sym memb doc &rest args)
|
|
58 t)
|
|
59 (defmacro defcustom (sym val doc &rest args)
|
|
60 `(defvar ,sym ,val ,doc))))
|
|
61
|
|
62 (defgroup whitespace nil
|
|
63 "Check for five different types of whitespaces in source code.
|
|
64
|
|
65 1. Leading space \(empty lines at the top of a file\).
|
|
66 2. Trailing space \(empty lines at the end of a file\).
|
|
67 3. Indentation space \(8 or more spaces at beginning of line, that should be
|
|
68 replaced with TABS\).
|
|
69 4. Spaces followed by a TAB. \(Almost always, we never want that\).
|
|
70 5. Spaces or TABS at the end of a line.
|
|
71
|
|
72 Whitespace errors are reported in a buffer, and on the modeline.
|
|
73
|
|
74 Modeline will show a W:<x> to denote a particular type of whitespace, where
|
|
75 `x' can be one \(or more\) of:
|
|
76
|
|
77 e - End-of-Line whitespace.
|
|
78 i - Indentation whitespace.
|
|
79 l - Leading whitespace.
|
|
80 s - Space followed by Tab.
|
|
81 t - Trailing whitespace.
|
|
82
|
|
83 "
|
|
84 ;; Since XEmacs doesn't have a 'convenience group, use the next best group
|
|
85 ;; which is 'editing?
|
|
86 :group (if whitespace-running-emacs 'convenience 'editing))
|
|
87
|
|
88 (defcustom whitespace-auto-cleanup nil
|
|
89 "Setting this will cleanup a buffer automatically on finding it whitespace
|
|
90 unclean.
|
|
91
|
|
92 Use the emacs `customize' command to set this.
|
|
93 "
|
|
94 :type 'boolean
|
|
95 :group 'whitespace)
|
|
96
|
|
97 (defcustom whitespace-silent nil
|
|
98 "Setting this to t will cause the whitespace error buffer not to pop
|
|
99 up. All whitespace errors will be shown only in the modeline.
|
|
100
|
|
101 Note that setting this may cause all whitespaces introduced in a file to go
|
|
102 unnoticed when the buffer is killed, unless the user visits the `*Whitespace
|
|
103 Errors*' buffer before opening \(or closing\) another file.
|
|
104
|
|
105 Use the emacs `customize' command to set this.
|
|
106 "
|
|
107 :type 'boolean
|
|
108 :group 'whitespace)
|
|
109
|
|
110 (defcustom whitespace-modes '(ada-mode asm-mode autoconf-mode awk-mode
|
|
111 c-mode c++-mode cc-mode cperl-mode
|
|
112 electric-nroff-mode emacs-lisp-mode
|
|
113 f90-mode fortran-mode html-mode
|
|
114 html3-mode java-mode jde-mode
|
|
115 ksh-mode latex-mode LaTeX-mode
|
|
116 lisp-mode m4-mode makefile-mode
|
|
117 modula-2-mode nroff-mode objc-mode
|
|
118 pascal-mode perl-mode prolog-mode
|
|
119 python-mode scheme-mode sgml-mode
|
|
120 sh-mode shell-script-mode
|
|
121 simula-mode tcl-mode tex-mode
|
|
122 texinfo-mode vrml-mode xml-mode)
|
|
123
|
|
124 "Modes that we check whitespace in. These are mostly programming and
|
|
125 documentation modes. But you may add other modes that you want whitespaces
|
|
126 checked in by adding something like the following to your `.emacs':
|
|
127
|
|
128 \(setq whitespace-modes \(cons 'my-mode \(cons 'my-other-mode
|
|
129 whitespace-modes\)\)
|
|
130
|
|
131 Or, alternately, you can use the Emacs `customize' command to set this.
|
|
132 "
|
|
133 :group 'whitespace)
|
|
134
|
|
135 (if whitespace-running-emacs (require 'timer))
|
|
136
|
|
137 (defvar whitespace-all-buffer-files nil
|
|
138 "An associated list of all buffers
|
|
139 and theirs files checked for whitespace cleanliness. This is to enable
|
|
140 periodic checking of whitespace cleanliness in the files visited by the
|
|
141 buffers.")
|
|
142
|
|
143 (defvar whitespace-rescan-timer nil
|
|
144 "Timer object that will be set to
|
|
145 rescan the files in Emacs buffers that have been modified.")
|
|
146
|
|
147 (defcustom whitespace-rescan-timer-time 60
|
|
148 "seconds after which
|
|
149 `whitespace-rescan-files-in-buffers' will check for modified files in Emacs
|
|
150 buffers."
|
|
151 :type 'integer
|
|
152 :group 'whitespace)
|
|
153
|
|
154
|
|
155 ;; Tell Emacs about this new kind of minor mode
|
|
156 (make-variable-buffer-local 'whitespace-mode)
|
|
157 (put 'whitespace-mode 'permanent-local nil)
|
|
158 (set-default 'whitespace-mode nil)
|
|
159
|
|
160 (make-variable-buffer-local 'whitespace-mode-line)
|
|
161 (put 'whitespace-mode-line 'permanent-local nil)
|
|
162 (set-default 'whitespace-mode-line nil)
|
|
163
|
|
164 (if (not (assoc 'whitespace-mode minor-mode-alist))
|
|
165 (setq minor-mode-alist (cons '(whitespace-mode whitespace-mode-line)
|
|
166 minor-mode-alist)))
|
|
167
|
|
168 (defun whitespace-check-whitespace-mode (&optional arg)
|
|
169 (if (null whitespace-mode)
|
|
170 (setq whitespace-mode
|
|
171 (if (or arg (member major-mode whitespace-modes))
|
|
172 t
|
|
173 nil))))
|
|
174
|
|
175 (defun whitespace-buffer (&optional quiet)
|
|
176 "Find five different types of white spaces in buffer:
|
|
177
|
|
178 1. Leading space \(empty lines at the top of a file\).
|
|
179 2. Trailing space \(empty lines at the end of a file\).
|
|
180 3. Indentation space \(8 or more spaces, that should be replaced with TABS\).
|
|
181 4. Spaces followed by a TAB. \(Almost always, we never want that\).
|
|
182 5. Spaces or TABS at the end of a line.
|
|
183
|
|
184 Check for whitespace only if this buffer really contains a non-empty file
|
|
185 and:
|
|
186 1. the major mode is one of the whitespace-modes, or
|
|
187 2. `whitespace-buffer' was explicitly called with a prefix argument.
|
|
188 "
|
|
189 (interactive)
|
|
190 (whitespace-check-whitespace-mode current-prefix-arg)
|
|
191 (if (and buffer-file-name (> (buffer-size) 0) whitespace-mode)
|
|
192 (progn
|
|
193 (whitespace-check-buffer-list (buffer-name) buffer-file-name)
|
|
194 (whitespace-tickle-timer)
|
|
195 (if whitespace-auto-cleanup
|
|
196 (if (and (not quiet) buffer-read-only)
|
|
197 (message "Can't Cleanup: %s is read-only." (buffer-name))
|
|
198 (whitespace-cleanup))
|
|
199 (let ((whitespace-leading (whitespace-buffer-leading))
|
|
200 (whitespace-trailing (whitespace-buffer-trailing))
|
|
201 (whitespace-indent (whitespace-buffer-search
|
|
202 whitespace-indent-regexp))
|
|
203 (whitespace-spacetab (whitespace-buffer-search
|
|
204 whitespace-spacetab-regexp))
|
|
205 (whitespace-ateol (whitespace-buffer-search
|
|
206 whitespace-ateol-regexp))
|
|
207 (whitespace-errmsg nil)
|
|
208 (whitespace-error nil)
|
|
209 (whitespace-filename buffer-file-name)
|
|
210 (whitespace-this-modeline ""))
|
|
211
|
|
212 ;; Now let's complain if we found any of the above.
|
|
213 (setq whitespace-error (or whitespace-leading whitespace-indent
|
|
214 whitespace-spacetab whitespace-ateol
|
|
215 whitespace-trailing))
|
|
216
|
|
217 (if whitespace-error
|
|
218 (progn
|
|
219 (setq whitespace-errmsg
|
|
220 (concat whitespace-filename " contains:\n"
|
|
221 (if whitespace-leading "Leading whitespace\n")
|
|
222 (if whitespace-indent
|
|
223 (concat "Indentation whitespace"
|
|
224 whitespace-indent "\n"))
|
|
225 (if whitespace-spacetab
|
|
226 (concat "Space followed by Tab"
|
|
227 whitespace-spacetab "\n"))
|
|
228 (if whitespace-ateol
|
|
229 (concat "End-of-line whitespace"
|
|
230 whitespace-ateol "\n"))
|
|
231 (if whitespace-trailing
|
|
232 "Trailing whitespace.\n")
|
|
233 "\ntype "
|
|
234 "`whitespace-cleanup' to cleanup the file."))
|
|
235 (setq whitespace-this-modeline
|
|
236 (concat (if whitespace-ateol "e")
|
|
237 (if whitespace-indent "i")
|
|
238 (if whitespace-leading "l")
|
|
239 (if whitespace-spacetab "s")
|
|
240 (if whitespace-trailing "t")))
|
|
241 (setq whitespace-mode-line
|
|
242 (concat " W:" whitespace-this-modeline))
|
|
243 (whitespace-force-mode-line-update)))
|
|
244 (save-excursion
|
|
245 (get-buffer-create whitespace-errbuf)
|
|
246 (kill-buffer whitespace-errbuf)
|
|
247 (get-buffer-create whitespace-errbuf)
|
|
248 (set-buffer whitespace-errbuf)
|
|
249 (if whitespace-errmsg
|
|
250 (progn
|
|
251 (insert whitespace-errmsg)
|
|
252 (if (not (and quiet whitespace-silent))
|
|
253 (display-buffer whitespace-errbuf t))
|
|
254 (if (not quiet)
|
|
255 (message "Whitespaces: [%s] in %s"
|
|
256 whitespace-this-modeline
|
|
257 whitespace-filename)))
|
|
258 (if (not quiet)
|
|
259 (message "%s clean" whitespace-filename)))))))))
|
|
260
|
|
261 (defun whitespace-region (s e)
|
|
262 "To check a region specified by point and mark for whitespace errors."
|
|
263 (interactive "r")
|
|
264 (save-excursion
|
|
265 (save-restriction
|
|
266 (narrow-to-region s e)
|
|
267 (whitespace-buffer))))
|
|
268
|
|
269 (defun whitespace-cleanup ()
|
|
270 "To cleanup the five different kinds of whitespace problems that
|
|
271 are defined in \\[whitespace-buffer]"
|
|
272 (interactive)
|
|
273 ;; If this buffer really contains a file, then run, else quit.
|
|
274 (whitespace-check-whitespace-mode current-prefix-arg)
|
|
275 (if (and buffer-file-name whitespace-mode)
|
|
276 (let ((whitespace-any nil)
|
|
277 (whitespace-tabwith 8)
|
|
278 (whitespace-tabwith-saved tab-width))
|
|
279
|
|
280 ;; since all printable TABS should be 8, irrespective of how
|
|
281 ;; they are displayed.
|
|
282 (setq tab-width whitespace-tabwith)
|
|
283
|
|
284 (if (whitespace-buffer-leading)
|
|
285 (progn
|
|
286 (whitespace-buffer-leading-cleanup)
|
|
287 (setq whitespace-any t)))
|
|
288
|
|
289 (if (whitespace-buffer-trailing)
|
|
290 (progn
|
|
291 (whitespace-buffer-trailing-cleanup)
|
|
292 (setq whitespace-any t)))
|
|
293
|
|
294 (if (whitespace-buffer-search whitespace-indent-regexp)
|
|
295 (progn
|
|
296 (whitespace-indent-cleanup)
|
|
297 (setq whitespace-any t)))
|
|
298
|
|
299 (if (whitespace-buffer-search whitespace-spacetab-regexp)
|
|
300 (progn
|
|
301 (whitespace-buffer-cleanup whitespace-spacetab-regexp "\t")
|
|
302 (setq whitespace-any t)))
|
|
303
|
|
304 (if (whitespace-buffer-search whitespace-ateol-regexp)
|
|
305 (progn
|
|
306 (whitespace-buffer-cleanup whitespace-ateol-regexp "")
|
|
307 (setq whitespace-any t)))
|
|
308
|
|
309 ;; Call this recursively till everything is taken care of
|
|
310 (if whitespace-any (whitespace-cleanup)
|
|
311 (progn
|
|
312 (message "%s clean" buffer-file-name)
|
|
313 (setq whitespace-mode-line nil)
|
|
314 (whitespace-force-mode-line-update)))
|
|
315 (setq tab-width whitespace-tabwith-saved))))
|
|
316
|
|
317 (defun whitespace-cleanup-region (s e)
|
|
318 "To do a whitespace cleanup on a region specified by point and mark."
|
|
319 (interactive "r")
|
|
320 (save-excursion
|
|
321 (save-restriction
|
|
322 (narrow-to-region s e)
|
|
323 (whitespace-cleanup))
|
|
324 (whitespace-buffer t)))
|
|
325
|
|
326 (defun whitespace-buffer-leading ()
|
|
327 "Check to see if there are any empty lines at the top of the file."
|
|
328 (save-excursion
|
|
329 (let ((pmin nil)
|
|
330 (pmax nil))
|
|
331 (goto-char (point-min))
|
|
332 (beginning-of-line)
|
|
333 (setq pmin (point))
|
|
334 (end-of-line)
|
|
335 (setq pmax (point))
|
|
336 (if (equal pmin pmax)
|
|
337 t
|
|
338 nil))))
|
|
339
|
|
340 (defun whitespace-buffer-leading-cleanup ()
|
|
341 "To remove any empty lines at the top of the file."
|
|
342 (save-excursion
|
|
343 (let ((pmin nil)
|
|
344 (pmax nil))
|
|
345 (goto-char (point-min))
|
|
346 (beginning-of-line)
|
|
347 (setq pmin (point))
|
|
348 (end-of-line)
|
|
349 (setq pmax (point))
|
|
350 (if (equal pmin pmax)
|
|
351 (progn
|
|
352 (kill-line)
|
|
353 (whitespace-buffer-leading-cleanup))))))
|
|
354
|
|
355 (defun whitespace-buffer-trailing ()
|
|
356 "Check to see if are is more than one empty line at the bottom."
|
|
357 (save-excursion
|
|
358 (let ((pmin nil)
|
|
359 (pmax nil))
|
|
360 (goto-char (point-max))
|
|
361 (beginning-of-line)
|
|
362 (setq pmin (point))
|
|
363 (end-of-line)
|
|
364 (setq pmax (point))
|
|
365 (if (equal pmin pmax)
|
|
366 (progn
|
|
367 (goto-char (- (point) 1))
|
|
368 (beginning-of-line)
|
|
369 (setq pmin (point))
|
|
370 (end-of-line)
|
|
371 (setq pmax (point))
|
|
372 (if (equal pmin pmax)
|
|
373 t
|
|
374 nil))
|
|
375 nil))))
|
|
376
|
|
377 (defun whitespace-buffer-trailing-cleanup ()
|
|
378 "Delete all the empty lines at the bottom."
|
|
379 (save-excursion
|
|
380 (let ((pmin nil)
|
|
381 (pmax nil))
|
|
382 (goto-char (point-max))
|
|
383 (beginning-of-line)
|
|
384 (setq pmin (point))
|
|
385 (end-of-line)
|
|
386 (setq pmax (point))
|
|
387 (if (equal pmin pmax)
|
|
388 (progn
|
|
389 (goto-char (1- pmin))
|
|
390 (beginning-of-line)
|
|
391 (setq pmin (point))
|
|
392 (end-of-line)
|
|
393 (setq pmax (point))
|
|
394 (if (equal pmin pmax)
|
|
395 (progn
|
|
396 (goto-char (1- (point-max)))
|
|
397 (beginning-of-line)
|
|
398 (kill-line)
|
|
399 (whitespace-buffer-trailing-cleanup))))))))
|
|
400
|
|
401 (defun whitespace-buffer-search (regexp)
|
|
402 "Search for any given whitespace REGEXP."
|
|
403 (let ((whitespace-retval ""))
|
|
404 (save-excursion
|
|
405 (goto-char (point-min))
|
|
406 (while (re-search-forward regexp nil t)
|
|
407 (setq whitespace-retval (format "%s %s " whitespace-retval
|
|
408 (match-beginning 0))))
|
|
409 (if (equal "" whitespace-retval)
|
|
410 nil
|
|
411 whitespace-retval))))
|
|
412
|
|
413 (defun whitespace-buffer-cleanup (regexp newregexp)
|
|
414 "Search for any given whitespace REGEXP and replace it with the NEWREGEXP."
|
|
415 (save-excursion
|
|
416 (goto-char (point-min))
|
|
417 (while (re-search-forward regexp nil t)
|
|
418 (replace-match newregexp))))
|
|
419
|
|
420 (defun whitespace-indent-cleanup ()
|
|
421 "Search for any 8 or more whitespaces at the beginning of a line and
|
|
422 replace it with tabs."
|
|
423 (interactive)
|
|
424 (save-excursion
|
|
425 (goto-char (point-min))
|
|
426 (while (re-search-forward whitespace-indent-regexp nil t)
|
|
427 (let ((column (current-column))
|
|
428 (indent-tabs-mode t))
|
|
429 (delete-region (match-beginning 0) (point))
|
|
430 (indent-to column)))))
|
|
431
|
|
432 ;; Force mode line updation for different Emacs versions
|
|
433 (defun whitespace-force-mode-line-update ()
|
|
434 "To Force the mode line update for different flavors of Emacs."
|
|
435 (if whitespace-running-emacs
|
|
436 (force-mode-line-update) ; Emacs
|
|
437 (redraw-modeline))) ; XEmacs
|
|
438
|
|
439 (defun whitespace-check-buffer-list (buf-name buf-file)
|
|
440 (if (and whitespace-mode (not (member (list buf-file buf-name)
|
|
441 whitespace-all-buffer-files)))
|
|
442 (add-to-list 'whitespace-all-buffer-files (list buf-file buf-name))))
|
|
443
|
|
444 (defun whitespace-tickle-timer ()
|
|
445 (if (not whitespace-rescan-timer)
|
|
446 (setq whitespace-rescan-timer
|
|
447 (if whitespace-running-emacs
|
|
448 (run-at-time nil whitespace-rescan-timer-time
|
|
449 'whitespace-rescan-files-in-buffers)
|
|
450 (add-timeout whitespace-rescan-timer-time
|
|
451 'whitespace-rescan-files-in-buffers nil
|
|
452 whitespace-rescan-timer-time)))))
|
|
453
|
|
454 (defun whitespace-rescan-files-in-buffers (&optional arg)
|
|
455 "Check to see if all the files that are whitespace clean are
|
|
456 actually clean still, if in buffers, or need rescaning."
|
|
457 (let ((whitespace-all-my-files whitespace-all-buffer-files)
|
|
458 buffile bufname thiselt buf)
|
|
459 (if (not whitespace-all-my-files)
|
|
460 (progn
|
|
461 (if whitespace-running-emacs
|
|
462 (cancel-timer whitespace-rescan-timer)
|
|
463 (disable-timeout whitespace-rescan-timer))
|
|
464 (setq whitespace-rescan-timer nil))
|
|
465 (while whitespace-all-my-files
|
|
466 (setq thiselt (car whitespace-all-my-files))
|
|
467 (setq whitespace-all-my-files (cdr whitespace-all-my-files))
|
|
468 (setq buffile (car thiselt))
|
|
469 (setq bufname (cadr thiselt))
|
|
470 (setq buf (get-buffer bufname))
|
|
471 (if (buffer-live-p buf)
|
|
472 (save-excursion
|
|
473 ;;(message "buffer %s live" bufname)
|
|
474 (set-buffer bufname)
|
|
475 (if whitespace-mode
|
|
476 (progn
|
|
477 ;;(message "checking for whitespace in %s" bufname)
|
|
478 (if whitespace-auto-cleanup
|
|
479 (progn
|
|
480 ;;(message "cleaning up whitespace in %s" bufname)
|
|
481 (whitespace-cleanup))
|
|
482 (progn
|
|
483 ;;(message "whitespace-buffer %s." (buffer-name))
|
|
484 (whitespace-buffer t))))
|
|
485 ;;(message "Removing %s from refresh list" bufname)
|
|
486 (whitespace-refresh-rescan-list buffile bufname)))
|
|
487 ;;(message "Removing %s from refresh list" bufname)
|
|
488 (whitespace-refresh-rescan-list buffile bufname))))))
|
|
489
|
|
490 (defun whitespace-refresh-rescan-list (buffile bufname)
|
|
491 "Refresh the list of files to be rescaned."
|
|
492 (if whitespace-all-buffer-files
|
|
493 (progn
|
|
494 (setq whitespace-all-buffer-files
|
|
495 (delete (list buffile bufname) whitespace-all-buffer-files)))
|
|
496 (progn
|
|
497 (if (and whitespace-running-emacs (timerp whitespace-rescan-timer))
|
|
498 (cancel-timer whitespace-rescan-timer))
|
|
499 (if (and (not whitespace-running-emacs) whitespace-rescan-timer)
|
|
500 (disable-timeout whitespace-rescan-timer))
|
|
501 (if whitespace-rescan-timer
|
|
502 (setq whitespace-rescan-timer nil)))))
|
|
503
|
|
504 (provide 'whitespace)
|
|
505
|
|
506 ;;; whitespace.el ends here
|