1 ;;; whitespace-visual-mode.el -- minor mode for making whitespace visible
3 ;; Copyright (C) 1994 - 2005 Dr. Heiko Muenkel
5 ;; Author: Dr. Heiko Muenkel <muenkel@tnt.uni-hannover.de>
6 ;; Keywords: modes, extensions
8 ;; This file is part of XEmacs.
10 ;; XEmacs is free software; you can redistribute it and/or modify it
11 ;; under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your
13 ;; option) any later version.
15 ;; XEmacs is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ;; General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs; if not, write to the Free Software
22 ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 ;;; Synched up with: [[ FSF 19.34. ]] Not in FSF.
29 ;; $Id: whitespace-visual-mode.el,v 1.1 2005-02-14 21:28:06 viteno Exp $
32 ;; This is a minor mode, which highlights whitespaces (blanks and
33 ;; tabs) with different faces, so that it is easier to
34 ;; distinguish between them.
35 ;; Toggle the mode with: M-x whitespace-visual-mode
36 ;; or with: M-x whitespace-visual-incremental-mode
37 ;; The second one should be used in big files.
39 ;; If you want to know how the whitespaces are highlighted then
40 ;; type: M-x whitespace-visual-show-faces
42 ;; There are 2 hook variables `whitespace-visual-incremental-mode-hook'
43 ;; and `whitespace-visual-mode-hook' to customize the mode.
45 ;; Look at the variable `whitespace-visual-chars', if you only want to
46 ;; highlight tabs or blanks and not both.
48 ;; Set `whitespace-visual-install-toolbar-icon' to t, if you want a
49 ;; toolbar icon for this mode.
51 ;; Set `whitespace-visual-install-submenu' to t, if you want a submenu
52 ;; for this mode. Sorry, at the moment there is no menu for the
55 ;; Thanks to Mike Scheidler for the toolbar icon code.
59 ;; The prefix of the package is now whitespace-visual
60 ;; instead of only whitespace. The new prefix whitespace-visual
61 ;; was choosen to avoid conflicts with another package called
62 ;; whitespace. Adding a whitespace toolbar icon should now work
63 ;; again. The whitespace submenu is now in the "Tools" menu
64 ;; instead of the old Apps menu.
68 ;; This file is part of the XEmacs package system. The following
69 ;; instructions belong to older versions of this mode.
71 ;; Put the files whitespace-visual-mode.el and adapt.el in one of your
72 ;; load-path directories and the following lines (without the
73 ;; comment signs) in your .emacs (adapt.el is already in the
76 ;; (autoload 'whitespace-visual-mode "whitespace-visual-mode"
77 ;; "Toggle whitespace visual mode.
78 ;; With arg, turn whitespace visual mode on iff arg is positive.
79 ;; In whitespace visual mode the different whitespaces (tab, blank return)
80 ;; are highlighted with different faces. The faces are:
81 ;; `whitespace-visual-blank-face', `whitespace-visual-tab-face' and
82 ;; `whitespace-visual-return-face'."
85 ;; (autoload 'whitespace-visual-incremental-mode "whitespace-visual-mode"
86 ;; "Toggle whitespace visual incremental mode.
87 ;; With arg, turn whitespace visual incremental mode on iff
88 ;; arg is positive. In whitespace visual incremental mode
89 ;; the different whitespaces (tab and blank) are highlighted with
90 ;; different faces. The faces are: `whitespace-blank-face' and
91 ;; `whitespace-visual-tab-face'. Use the command
92 ;; `whitespace-visual-show-faces' to show their values.
93 ;; In this mode only these tabs and blanks are highlighted, which are in
94 ;; the region from (point) - (window-height) to (point) +
99 (provide 'whitespace-visual-mode)
100 ;; We don't need adapt
105 (defgroup whitespace-visual nil
106 "Minor mode for making whitespaces visible"
111 (defcustom whitespace-visual-mode nil
112 "Non-nil, if the `whitespace-visual-mode' is active."
114 :set (lambda (symbol value)
115 (whitespace-visual-mode (or value 0)))
116 :require 'whitespace-visual-mode
117 :initialize 'custom-initialize-default
118 :group 'whitespace-visual)
120 (make-variable-buffer-local 'whitespace-visual-mode)
123 (defcustom whitespace-visual-mode-line-string " WSP"
124 "*String displayed on the modeline when whitespace-visual-mode is active.
125 Set this to nil if you don't want a modeline indicator."
126 :group 'whitespace-visual
130 (defcustom whitespace-visual-incremental-mode-line-string " WSPI"
131 "*String displayed on the modeline when whitespace-visual-incremental-mode
132 is active. Set this to nil if you don't want a modeline indicator."
133 :group 'whitespace-visual
136 (defcustom whitespace-visual-chars 'tabs-and-blanks
137 "*Determines, which whitespaces are highlighted.
139 'tabs-and-blanks => tabs and blanks are highlighted;
140 'tabs => only tabs are highlighted;
141 'blanks => only blanks are highlighted;.
143 Changing this variable during the whitespace-visual-*-mode is active could lead
144 to wrong highlighted whitespaces."
145 :type '(radio (const tabs-and-blanks)
148 :group 'whitespace-visual)
150 (make-variable-buffer-local 'whitespace-visual-chars)
152 (defcustom whitespace-visual-mode-hook nil
153 "*Run after the `whitespace-visual-mode' is switched on."
155 :group 'whitespace-visual)
157 (defcustom whitespace-visual-incremental-mode-hook nil
158 "*Run after the `whitespace-visual-incremental-mode' is switched on."
160 :group 'whitespace-visual)
162 ;; We don't need adapt.
163 ;; (if (adapt-xemacsp)
166 (defcustom whitespace-visual-install-toolbar-icon nil
167 "Set it to t, if a toolbar icon should be installed during loading this file.
168 The icon calls the function 'whitespace-visual-toolbar-function'."
170 :group 'whitespace-visual)
172 (defcustom whitespace-visual-install-submenu nil
173 "Set it to t, if a submenu should be installed during loading this file."
175 :group 'whitespace-visual)
180 (defcustom whitespace-visual-toolbar-function
181 'whitespace-visual-incremental-mode
182 "*The toolbar icon for the whitespace visual mode calls this function.
184 'whitespace-visual--mode and 'whitespace-visual-incremental-mode."
186 :group 'whitespace-visual)
188 (defcustom whitespace-visual-blank-and-tab-search-string "\\( \\)\\|\\(\t\\)"
189 "The regexp used to search for tabs and blanks."
191 :group 'whitespace-visual)
193 (defcustom whitespace-visual-tab-search-string "\t"
194 "The search string used to find tabs."
196 :group 'whitespace-visual)
198 (defcustom whitespace-visual-blank-search-string " "
199 "The search string used to find blanks."
201 :group 'whitespace-visual)
203 (defface whitespace-visual-blank-face
205 (:background "LightBlue1")))
206 "Face to show blanks with"
207 :group 'whitespace-visual)
209 (defface whitespace-visual-tab-face
211 (:background "yellow" :underline t)))
212 "Face to show TABs with"
213 :group 'whitespace-visual)
215 (defun whitespace-visual-show-faces ()
216 "Shows the faces used by the `whitespace-visual-mode'."
219 (let ((actual-buffer-name (buffer-name (current-buffer)))
220 (actual-whitespace-chars whitespace-visual-chars)
221 (whitespace-visual-mode-active
222 (or whitespace-visual-mode
223 whitespace-visual-incremental-mode))
224 (buffer (get-buffer-create "*Help*")))
226 (setq whitespace-visual-chars actual-whitespace-chars)
227 (delete-region (point-min) (point-max))
228 (insert "In the whitespace visual minor mode\n"
230 (whitespace-visual-highlight-region (1- (point)) (point))
231 (insert "\" is a blank, highlighted with "
232 "`whitespace-visual-blank-face' and\n"
234 (whitespace-visual-highlight-region (1- (point)) (point))
235 (insert "\" is a tab, highlighted with `whitespace-visual-tab-face'.")
238 (if (eq whitespace-visual-chars 'blanks)
240 "The highlighting of tabs is switched off.\n")
241 (if (eq whitespace-visual-chars 'tabs)
243 "The highlighting of blanks is switched off.\n")))
245 (if whitespace-visual-mode-active
246 (insert "A whitespace-visual minor mode is active in the buffer\n "
249 (insert "No whitespace-visual minor mode is active in the buffer\n "
252 (show-temp-buffer-in-current-frame buffer)
256 (defun whitespace-visual-highlight-chars-in-region (char-string from to face)
257 "Highlights the CHAR-STRING in the region from FROM to TO with the FACE."
258 (while (search-forward char-string end t)
260 (cond ((match-beginning 0)
261 (setq extent (make-extent (match-beginning 0) (match-end 0)))
262 (set-extent-face extent face)
264 (set-extent-property extent 'start-open t)
265 (set-extent-property extent 'end-open t)
268 (defun whitespace-visual-highlight-region (from to)
269 "Highlights the whitespaces in the region from FROM to TO."
270 (let ((start (min from to))
273 ;; (message "Highlighting tabs and blanks...")
275 (cond ((eq whitespace-visual-chars 'tabs-and-blanks)
276 (while (search-forward-regexp
277 whitespace-visual-blank-and-tab-search-string end t)
279 (cond ((match-beginning 1) ; blanks ?
280 (setq extent (make-extent (match-beginning 1)
282 (set-extent-face extent 'whitespace-visual-blank-face)
284 ((match-beginning 2) ; tabs ?
285 (setq extent (make-extent (match-beginning 2)
287 (set-extent-face extent 'whitespace-visual-tab-face)
290 (set-extent-property extent 'start-open t)
291 (set-extent-property extent 'end-open t)
293 ((eq whitespace-visual-chars 'tabs)
294 (whitespace-visual-highlight-chars-in-region
295 whitespace-visual-tab-search-string
298 'whitespace-visual-tab-face))
299 ((eq whitespace-visual-chars 'blanks)
300 (whitespace-visual-highlight-chars-in-region
301 whitespace-visual-blank-search-string
304 'whitespace-visual-blank-face))
305 (t (error "ERROR: Bad value of whitespace-visual-highlight-char")))
308 (defun whitespace-visual-highlight-buffer ()
309 "Highlights the whitespaces in the current buffer."
310 (whitespace-visual-highlight-region (point-min) (point-max))
313 (defsubst whitespace-visual-find-next-highlighted-region (from to)
314 "Returns nil or the next highlighted region."
315 (map-extents '(lambda (extent dummy)
316 (if (extent-property extent
317 'whitespace-visual-highlighted-region)
323 (defun whitespace-visual-incremental-highlight (from to)
324 "Highlights the region from FROM to TO incremental."
327 (let ((extent (extent-at (point) nil 'whitespace-visual-highlighted-region))
330 (while (< (point) to)
332 (goto-char (extent-end-position extent)))
337 (setq next-extent (whitespace-visual-find-next-highlighted-region
343 (set-extent-endpoints extent
344 (extent-start-position extent)
345 (extent-end-position next-extent)
347 (whitespace-visual-highlight-region start
349 (extent-start-position
351 (delete-extent next-extent))
352 (set-extent-endpoints extent
353 (extent-start-position extent)
355 (whitespace-visual-highlight-region start to))
358 (setq extent next-extent)
359 (whitespace-visual-highlight-region
361 (1- (extent-start-position
363 (set-extent-endpoints extent
365 (extent-end-position next-extent)))
366 (setq extent (make-extent start to))
367 (set-extent-property extent
368 'whitespace-visual-highlighted-region t)
369 (whitespace-visual-highlight-region start to)))
373 (defun whitespace-visual-highlight-window ()
374 "Highlights the whitespaces in the current window."
375 (whitespace-visual-incremental-highlight
377 (forward-line (- (window-height)))
380 (forward-line (window-height))
383 (defun whitespace-visual-dehighlight-region (start end)
384 "Dehighlights the whitespaces in the region from START to END."
385 (map-extents '(lambda (extent dummy)
386 (if (or (eq (extent-face extent)
387 'whitespace-visual-blank-face)
388 (eq (extent-face extent)
389 'whitespace-visual-tab-face)
392 'whitespace-visual-highlighted-region))
394 (delete-extent extent)
402 (defun whitespace-visual-dehighlight-buffer ()
403 "Dehighlights the whitespaces in the current buffer."
404 (whitespace-visual-dehighlight-region (point-min) (point-max))
407 (defun whitespace-visual-highlight-after-change-function (beg end len)
408 "Called, when any modification is made to buffer text. Highlights
409 the whitespaces (blanks and tabs) in the region from BEG to
410 END. LEN isn't used, but provided from the after-change hook."
412 (null whitespace-visual-mode))
414 (whitespace-visual-dehighlight-region beg end)
415 (whitespace-visual-highlight-region beg end)))
418 (defun whitespace-visual-mode (&optional arg)
419 "Toggle whitespace visual mode.
420 With arg, turn whitespace visual mode on iff arg is positive.
421 In whitespace visual mode the different whitespaces (tab and blank)
422 are highlighted with different faces. The faces are:
423 `whitespace-visual-blank-face' and `whitespace-visual-tab-face'.
424 Use the command `whitespace-visual-show-faces' to show their values."
426 (setq whitespace-visual-mode
427 (if (null arg) (not whitespace-visual-mode)
428 (> (prefix-numeric-value arg) 0)))
429 (if (and whitespace-visual-mode whitespace-visual-incremental-mode)
431 (whitespace-visual-incremental-highlight (point-min) (point-max))
432 (setq whitespace-visual-incremental-mode nil)
433 (remove-hook 'post-command-hook 'whitespace-visual-highlight-window)
434 (run-hooks 'whitespace-visual-mode-hook)
436 (setq whitespace-visual-incremental-mode nil)
437 (remove-hook 'post-command-hook 'whitespace-visual-highlight-window)
438 (redraw-modeline) ;(force-mode-line-update)
439 (if whitespace-visual-mode
441 (whitespace-visual-highlight-buffer)
442 (make-local-variable 'after-change-functions)
443 (add-hook 'after-change-functions
444 'whitespace-visual-highlight-after-change-function)
445 (run-hooks 'whitespace-visual-mode-hook))
446 (whitespace-visual-dehighlight-buffer)
447 (remove-hook 'after-change-functions
448 'whitespace-visual-highlight-after-change-function)
449 (remove-hook 'post-command-hook 'whitespace-visual-highlight-window)
452 (defvar whitespace-visual-incremental-mode nil
453 "Non-nil, if the `whitespace-visual-incremental-mode' is active.")
455 (make-variable-buffer-local 'whitespace-visual-incremental-mode)
458 (defun whitespace-visual-incremental-mode (&optional arg)
459 "Toggle whitespace visual incremental mode.
460 With arg, turn whitespace-visual incremental mode on iff arg is positive.
461 In whitespace visual incremental mode the different whitespaces (tab and
462 blank) are highlighted with different faces. The faces are:
463 `whitespace-visual-blank-face' and `whitespace-visual-tab-face'.
464 Use the command `whitespace-visual-show-faces' to show their values.
465 In this mode only these tabs and blanks are highlighted, which are in
466 the region from (point) - (window-height) to (point) + (window-height)."
468 (setq whitespace-visual-incremental-mode
469 (if (null arg) (not whitespace-visual-incremental-mode)
470 (> (prefix-numeric-value arg) 0)))
471 (if (and whitespace-visual-mode whitespace-visual-incremental-mode)
472 (set-extent-property (make-extent (point-min) (point-max))
473 'whitespace-visual-highlighted-region
475 (setq whitespace-visual-mode nil)
476 (redraw-modeline) ;(force-mode-line-update)
477 ;(set-buffer-modified-p (buffer-modified-p)) ;No-op, but updates mode line.
478 (if whitespace-visual-incremental-mode
480 (whitespace-visual-highlight-window)
481 (make-local-variable 'post-command-hook)
482 (add-hook 'post-command-hook 'whitespace-visual-highlight-window)
483 (make-local-variable 'after-change-functions)
484 (add-hook 'after-change-functions
485 'whitespace-visual-highlight-after-change-function)
486 (run-hooks 'whitespace-visual-incremental-mode-hook))
487 (whitespace-visual-dehighlight-buffer)
488 (remove-hook 'after-change-functions
489 'whitespace-visual-highlight-after-change-function)
490 (remove-hook 'post-command-hook 'whitespace-visual-highlight-window)
494 ;;; Add whitespace-visual-mode and whitespace-visual-incremental-mode
495 ;;; to the minor-mode-alist
498 (if (fboundp 'add-minor-mode)
500 (add-minor-mode 'whitespace-visual-mode
501 whitespace-visual-mode-line-string)
502 (add-minor-mode 'whitespace-visual-incremental-mode
503 whitespace-visual-incremental-mode-line-string))
504 (or (assq 'whitespace-visual-mode minor-mode-alist)
505 (setq minor-mode-alist
506 (cons '(whitespace-visual-mode whitespace-visual-mode-line-string)
508 (or (assq 'whitespace-visual-incremental-mode minor-mode-alist)
509 (setq minor-mode-alist
510 (cons '(whitespace-visual-incremental-mode
511 whitespace-visual-incremental-mode-line-string)
514 ;;; Menu for the whitespace-visual mode
516 (defun whitespace-visual-set-whitespace-chars (new-whitespace-chars)
517 "Sets the variable `whitespace-visual-chars' and activates the change."
521 (completing-read "Whitespaces to highlight: "
522 '(("tabs-and-blanks")
527 (symbol-name 'whitespace-visual-chars)))))
528 (if (eq whitespace-visual-chars new-whitespace-chars)
530 (setq whitespace-visual-chars new-whitespace-chars)
531 (setq-default whitespace-visual-chars new-whitespace-chars)
532 (cond (whitespace-visual-mode (whitespace-visual-mode)
533 (whitespace-visual-mode))
534 (whitespace-visual-incremental-mode
535 (whitespace-visual-incremental-mode)
536 (whitespace-visual-incremental-mode))
539 (defvar whitespace-visual-menu nil
540 "A menu for the whitespace visual minor mode.")
542 (setq whitespace-visual-menu
544 ["Highlight Whitespaces"
545 whitespace-visual-mode
547 :selected whitespace-visual-mode]
548 ["Incremental Highlighting"
549 whitespace-visual-incremental-mode
551 :selected whitespace-visual-incremental-mode
554 ["Show Whitespace Faces" whitespace-visual-show-faces t]
556 ["Highlight Tabs & Blanks"
557 (whitespace-visual-set-whitespace-chars 'tabs-and-blanks)
559 :selected (eq whitespace-visual-chars 'tabs-and-blanks)]
560 ["Highlight Only Tabs"
561 (whitespace-visual-set-whitespace-chars 'tabs)
563 :selected (eq whitespace-visual-chars 'tabs)]
564 ["Highlight Only Blanks"
565 (whitespace-visual-set-whitespace-chars 'blanks)
567 :selected (eq whitespace-visual-chars 'blanks)]
570 (if (and (boundp 'whitespace-visual-install-submenu)
571 whitespace-visual-install-submenu)
572 (add-submenu '("Tools") whitespace-visual-menu))
574 ;;; Toolbar icon for the XEmacs
576 (if (featurep 'toolbar)
578 (defvar toolbar-wspace-icon
579 (toolbar-make-button-list
581 static char * whitespace[] = {
583 \" c Gray75 s backgroundToolBarColor\",
584 \". c black s foregroundToolBarColor\",
594 \" .. .. ..o.o..oo..X... .. \",
595 \" . . X.o..o.ooX. X. . . \",
596 \" . . .oo.oo.ooX.XX. .... \",
597 \" ... .oo.oo.ooo.oo. . \",
598 \" . .Xoo.oo.ooo.oo. . . \",
599 \" . .Xo...o..o...o.. .. \",
600 \" XooooooooooooX \",
601 \" XooooooooooooX \",
602 \" .... ....ooo...ooo... .. \",
603 \" . . .oo.o.oo.oo.oX. . . \",
604 \" . .oo.ooo..oo.oX .... \",
605 \" .. .oo.o..o.oo.oX . \",
606 \" . . .oo.o.oo.oo.oX. . . \",
607 \" .... ...oo.....oo.. .. \",
608 \" .ooooooooooooX \",
609 \" .XXXXXXXXXXXXX \",
616 "A whitespace icon.")
619 (defun whitespace-visual-toolbar-function ()
620 "Calls the function determined by `whitespace-visual-toolbar-function'."
622 (call-interactively whitespace-visual-toolbar-function))
624 (if (and (featurep 'xemacs) ;; (adapt-xemacsp)
625 whitespace-visual-install-toolbar-icon
627 (eq (device-type (selected-device)) 'x))
628 (let ((tb (mapcar #'(lambda (e)
629 (elt e 1)) (specifier-instance default-toolbar))))
630 (and (not (member 'whitespace-visual-toolbar-function tb))
634 (make-toolbar-instantiator default-toolbar)
635 [toolbar-wspace-icon whitespace-visual-toolbar-function
636 t "Toggle whitespace visual mode"]
638 (position 'toolbar-replace tb)
639 (position 'toolbar-undo tb)
640 (position 'toolbar-paste tb)
641 (position 'toolbar-copy tb)
642 (position 'toolbar-cut tb))))
643 (if n (1+ n) (length tb))))))))
645 ;;; whitespace-visual-mode.el ends here