;;; visws.el --- Make whitespace visible    -*- coding: latin-1 -*-
;; Copyright (C) 2001 Free Software Foundation, Inc.
;; Author: Miles Bader <>
;; Keywords: convenience

;; GNU Emacs 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 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:
;; Defines the mode `visible-whitespace-mode'.
;; When active, normally invisible whitespace characters are made visible.

;;; Code:

(require 'latin1-disp)

(defface visible-whitespace '((t :foreground "blue" :bold t))
  "Face for control-characters revealed by `visible-whitespace-mode'.")

(defcustom visible-whitespace-mappings
  '((?\n [?$ ?\n])
    ;; Note that TAB still tabs, but with a graphic indicator before the
    ;; tab; we only use single-character graphic indicator to reduce the
    ;; number of cases where the indicator causes the tabbing to be
    ;; screwed up.
    (?\t [?» ?\t] [?\\ ?\t])
    (?   [?·] [?.]))
  "An alist of mappings for displaying whitespace in `visible-whitespace-mode'.

The car of each mapping is a whitespace character, and the cdr is a list of
display vectors (a vector of characters).  The first display vector the
list that can be displayed is used; if no display vector for a mapping can
be displayed, then that character is displayed unmodified.

The characters in are displayed using the `visible-whitespace' face."
  :type 'list)

(defun visws-legal-display-vector-p (vec)
  "Return true if every character in the display vector VEC can be displayed."
  (let ((i 0) (len (length vec)))
    (while (and (< i len) (latin1-char-displayable-p (aref vec i)))
      (setq i (1+ i)))
    (= i len)))

;; Buffer local variable used to remember whether a buffer initially had
;; a local display table or not.
(defvar visws-display-table-was-local nil)

(define-minor-mode visible-whitespace-mode
  "Toggle Visible Whitespace mode
When active, normally invisible whitespace characters are made visible.

With prefix argument ARG, turn on if positive, otherwise off.
Returns non-nil if the new state is enabled."
  " VisWS"
  (if visible-whitespace-mode
      (let ((face-bits (ash (face-id 'visible-whitespace) 19)))
		(set (make-local-variable 'visws-display-table-was-local)
		(unless buffer-display-table
		  (setq buffer-display-table (make-display-table)))
		(dolist (entry visible-whitespace-mappings)
		  (let ((vecs (cdr entry)))
			(while (and vecs (not (visws-legal-display-vector-p (car vecs))))
			  (setq vecs (cdr vecs)))
			(when vecs
			  (let ((vec (copy-sequence (car vecs))))
				(dotimes (i (length vec))
				  (when (not (eq (aref vec i) (car entry)))
					(aset vec i (logior (aref vec i) face-bits))))
				(aset buffer-display-table (car entry) vec))))))
    (if visws-display-table-was-local
		(dolist (entry visible-whitespace-mappings)
		  (aset buffer-display-table (car entry) nil))
      (setq buffer-display-table nil))))

;;; visws.el ends here