Mercurial > emacs
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 |