comparison lisp/whitespace.el @ 25582:e8428725fec3

Initial revision
author Dave Love <fx@gnu.org>
date Tue, 07 Sep 1999 11:28:47 +0000
parents
children c0e27d3ce645
comparison
equal deleted inserted replaced
25581:a066c27ca69e 25582:e8428725fec3
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