X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/a9989764a4d54bf58381d5c3902e575bdf314245..c23dcd9fa3d1dd3b6e72cc4627808d13cd44d579:/lisp/whitespace.el diff --git a/lisp/whitespace.el b/lisp/whitespace.el index 1b737b5bb0..4504c4c9f1 100644 --- a/lisp/whitespace.el +++ b/lisp/whitespace.el @@ -6,25 +6,23 @@ ;; Author: Vinicius Jose Latorre ;; Maintainer: Vinicius Jose Latorre ;; Keywords: data, wp -;; Version: 9.2 +;; Version: 11.2.2 ;; X-URL: http://www.emacswiki.org/cgi-bin/wiki/ViniciusJoseLatorre ;; This file is part of GNU Emacs. -;; 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 3, or (at your -;; option) any later version. +;; 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 3 of the License, 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 -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;; General Public License for more details. +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; 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., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -46,8 +44,8 @@ ;; it provides a visual mark for characters, for example, at the end ;; of line (?\xB6), at SPACEs (?\xB7) and at TABs (?\xBB). ;; -;; The `whitespace-style' and `whitespace-chars' variables are used to -;; select which way should be used to visualize blanks. +;; The `whitespace-style' variable selects which way blanks are +;; visualized. ;; ;; Note that when whitespace is turned on, whitespace saves the ;; font-lock state, that is, if font-lock is on or off. And @@ -162,37 +160,62 @@ ;; ;; There are also the following useful commands: ;; +;; `whitespace-newline-mode' +;; Toggle NEWLINE minor mode visualization ("nl" on modeline). +;; +;; `global-whitespace-newline-mode' +;; Toggle NEWLINE global minor mode visualization ("NL" on modeline). +;; +;; `whitespace-report' +;; Report some blank problems in buffer. +;; +;; `whitespace-report-region' +;; Report some blank problems in a region. +;; ;; `whitespace-cleanup' ;; Cleanup some blank problems in all buffer or at region. ;; ;; `whitespace-cleanup-region' ;; Cleanup some blank problems at region. ;; -;; `whitespace-buffer' -;; Turn on `whitespace-mode' forcing some settings. -;; ;; The problems, which are cleaned up, are: ;; ;; 1. empty lines at beginning of buffer. ;; 2. empty lines at end of buffer. -;; If `whitespace-chars' has `empty' as an element, remove all +;; If `whitespace-style' includes the value `empty', remove all ;; empty lines at beginning and/or end of buffer. ;; ;; 3. 8 or more SPACEs at beginning of line. -;; If `whitespace-chars' has `indentation' as an element, replace 8 -;; or more SPACEs at beginning of line by TABs. +;; If `whitespace-style' includes the value `indentation': +;; replace 8 or more SPACEs at beginning of line by TABs, if +;; `indent-tabs-mode' is non-nil; otherwise, replace TABs by +;; SPACEs. +;; If `whitespace-style' includes the value `indentation::tab', +;; replace 8 or more SPACEs at beginning of line by TABs. +;; If `whitespace-style' includes the value `indentation::space', +;; replace TABs by SPACEs. ;; ;; 4. SPACEs before TAB. -;; If `whitespace-chars' has `space-before-tab' as an element, -;; replace SPACEs by TABs. +;; If `whitespace-style' includes the value `space-before-tab': +;; replace SPACEs by TABs, if `indent-tabs-mode' is non-nil; +;; otherwise, replace TABs by SPACEs. +;; If `whitespace-style' includes the value +;; `space-before-tab::tab', replace SPACEs by TABs. +;; If `whitespace-style' includes the value +;; `space-before-tab::space', replace TABs by SPACEs. ;; ;; 5. SPACEs or TABs at end of line. -;; If `whitespace-chars' has `trailing' as an element, remove all -;; SPACEs or TABs at end of line." +;; If `whitespace-style' includes the value `trailing', remove all +;; SPACEs or TABs at end of line. ;; ;; 6. 8 or more SPACEs after TAB. -;; If `whitespace-chars' has `space-after-tab' as an element, +;; If `whitespace-style' includes the value `space-after-tab': +;; replace SPACEs by TABs, if `indent-tabs-mode' is non-nil; +;; otherwise, replace TABs by SPACEs. +;; If `whitespace-style' includes the value `space-after-tab::tab', ;; replace SPACEs by TABs. +;; If `whitespace-style' includes the value +;; `space-after-tab::space', replace TABs by SPACEs. ;; ;; ;; Hooks @@ -216,9 +239,7 @@ ;; Below it's shown a brief description of whitespace options, please, ;; see the options declaration in the code for a long documentation. ;; -;; `whitespace-style' Specify the visualization style. -;; -;; `whitespace-chars' Specify which kind of blank is +;; `whitespace-style' Specify which kind of blank is ;; visualized. ;; ;; `whitespace-space' Face used to visualize SPACE. @@ -276,13 +297,28 @@ ;; `whitespace-display-mappings' Specify an alist of mappings ;; for displaying characters. ;; -;; `whitespace-global-modes' Modes for which global `whitespace-mode' is -;; automagically turned on. +;; `whitespace-global-modes' Modes for which global +;; `whitespace-mode' is automagically +;; turned on. +;; +;; `whitespace-action' Specify which action is taken when a +;; buffer is visited or written. ;; ;; ;; Acknowledgements ;; ---------------- ;; +;; Thanks to David Reitter for suggesting a +;; `whitespace-newline' initialization with low contrast relative to +;; the background color. +;; +;; Thanks to Stephen Deasey for the +;; `indent-tabs-mode' usage suggestion. +;; +;; Thanks to Eric Cooper for the suggestion to have hook +;; actions when buffer is written as the original whitespace package +;; had. +;; ;; Thanks to nschum (EmacsWiki) for the idea about highlight "long" ;; lines tail. See EightyColumnRule (EmacsWiki). ;; @@ -305,7 +341,7 @@ ;; "long" lines. See EightyColumnRule (EmacsWiki). ;; ;; Thanks to Yanghui Bian for indicating a new -;; newline character mapping. +;; NEWLINE character mapping. ;; ;; Thanks to Pete Forman for indicating ;; whitespace-mode.el on XEmacs. @@ -336,99 +372,145 @@ (defgroup whitespace nil "Visualize blanks (TAB, (HARD) SPACE and NEWLINE)." :link '(emacs-library-link :tag "Source Lisp File" "whitespace.el") - :version "22.2" + :version "23.1" :group 'wp :group 'data) -(defcustom whitespace-style '(mark color) - "*Specify the visualization style. - -It's a list which element value can be: - - mark display mappings are visualized. - - color faces are visualized. - -Any other value is ignored. - -If nil, don't visualize TABs, (HARD) SPACEs and NEWLINEs. - -See also `whitespace-display-mappings' for documentation." - :type '(repeat :tag "Style of Blank" - (choice :tag "Style of Blank" - (const :tag "Display Table" mark) - (const :tag "Faces" color))) - :group 'whitespace) - - -(defcustom whitespace-chars +(defcustom whitespace-style '(tabs spaces trailing lines space-before-tab newline - indentation empty space-after-tab) + indentation empty space-after-tab + space-mark tab-mark newline-mark) "*Specify which kind of blank is visualized. -It's a list which element value can be: +It's a list containing some or all of the following values: - trailing trailing blanks are visualized. + trailing trailing blanks are visualized via faces. - tabs TABs are visualized. + tabs TABs are visualized via faces. - spaces SPACEs and HARD SPACEs are visualized. + spaces SPACEs and HARD SPACEs are visualized via + faces. lines lines whose have columns beyond - `whitespace-line-column' are highlighted. + `whitespace-line-column' are highlighted via + faces . Whole line is highlighted. - It has precedence over - `lines-tail' (see below). + It has precedence over `lines-tail' (see + below). lines-tail lines whose have columns beyond - `whitespace-line-column' are highlighted. + `whitespace-line-column' are highlighted via + faces. But only the part of line which goes beyond `whitespace-line-column' column. It has effect only if `lines' (see above) - is not present in `whitespace-chars'. + is not present in `whitespace-style'. + + newline NEWLINEs are visualized via faces. + + empty empty lines at beginning and/or end of buffer + are visualized via faces. - space-before-tab SPACEs before TAB are visualized. + indentation::tab 8 or more SPACEs at beginning of line are + visualized via faces. - newline NEWLINEs are visualized. + indentation::space TABs at beginning of line are visualized via + faces. indentation 8 or more SPACEs at beginning of line are - visualized. + visualized, if `indent-tabs-mode' (which see) + is non-nil; otherwise, TABs at beginning of + line are visualized via faces. - empty empty lines at beginning and/or end of buffer - are visualized. + space-after-tab::tab 8 or more SPACEs after a TAB are + visualized via faces. + + space-after-tab::space TABs are visualized when occurs 8 or + more SPACEs after a TAB via faces. + + space-after-tab 8 or more SPACEs after a TAB are + visualized, if `indent-tabs-mode' + (which see) is non-nil; otherwise, + the TABs are visualized via faces. - space-after-tab 8 or more SPACEs after a TAB are visualized. + space-before-tab::tab SPACEs before TAB are visualized via + faces. + + space-before-tab::space TABs are visualized when occurs SPACEs + before TAB via faces. + + space-before-tab SPACEs before TAB are visualized, if + `indent-tabs-mode' (which see) is + non-nil; otherwise, the TABs are + visualized via faces. + + space-mark SPACEs and HARD SPACEs are visualized via + display table. + + tab-mark TABs are visualized via display table. + + newline-mark NEWLINEs are visualized via display table. Any other value is ignored. -If nil, don't visualize TABs, (HARD) SPACEs and NEWLINEs. +If nil, don't visualize TABs, (HARD) SPACEs and NEWLINEs via faces and +via display table. + +There is an evaluation order for some values, if some values are +included in `whitespace-style' list. For example, if +indentation, indentation::tab and/or indentation::space are +included in `whitespace-style' list. The evaluation order for +these values is: + + * For indentation: + 1. indentation + 2. indentation::tab + 3. indentation::space -Used when `whitespace-style' has `color' as an element. -Used also when `whitespace-chars' has `newline' as an element and -`whitespace-style' has `mark' as an element." + * For SPACEs after TABs: + 1. space-after-tab + 2. space-after-tab::tab + 3. space-after-tab::space + + * For SPACEs before TABs: + 1. space-before-tab + 2. space-before-tab::tab + 3. space-before-tab::space + +So, for example, if indentation and indentation::space are +included in `whitespace-style' list, the indentation value is +evaluated instead of indentation::space value. + +See also `whitespace-display-mappings' for documentation." :type '(repeat :tag "Kind of Blank" - (choice :tag "Kind of Blank" - (const :tag "Trailing TABs, SPACEs and HARD SPACEs" + (choice :tag "Kind of Blank Face" + (const :tag "(Face) Trailing TABs, SPACEs and HARD SPACEs" trailing) - (const :tag "SPACEs and HARD SPACEs" spaces) - (const :tag "TABs" tabs) - (const :tag "Lines" lines) - (const :tag "SPACEs before TAB" + (const :tag "(Face) SPACEs and HARD SPACEs" + spaces) + (const :tag "(Face) TABs" tabs) + (const :tag "(Face) Lines" lines) + (const :tag "(Face) SPACEs before TAB" space-before-tab) - (const :tag "NEWLINEs" newline) - (const :tag "Indentation SPACEs" indentation) - (const :tag "Empty Lines At BOB And/Or EOB" + (const :tag "(Face) NEWLINEs" newline) + (const :tag "(Face) Indentation SPACEs" + indentation) + (const :tag "(Face) Empty Lines At BOB And/Or EOB" empty) - (const :tag "SPACEs after TAB" - space-after-tab))) + (const :tag "(Face) SPACEs after TAB" + space-after-tab) + (const :tag "(Mark) SPACEs and HARD SPACEs" + space-mark) + (const :tag "(Mark) TABs" tab-mark) + (const :tag "(Mark) NEWLINEs" newline-mark))) :group 'whitespace) (defcustom whitespace-space 'whitespace-space "*Symbol face used to visualize SPACE. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `spaces'." :type 'face :group 'whitespace) @@ -446,7 +528,7 @@ Used when `whitespace-style' has `color' as an element." (defcustom whitespace-hspace 'whitespace-hspace "*Symbol face used to visualize HARD SPACE. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `spaces'." :type 'face :group 'whitespace) @@ -464,7 +546,7 @@ Used when `whitespace-style' has `color' as an element." (defcustom whitespace-tab 'whitespace-tab "*Symbol face used to visualize TAB. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `tabs'." :type 'face :group 'whitespace) @@ -484,18 +566,18 @@ Used when `whitespace-style' has `color' as an element." See `whitespace-display-mappings'. -Used when `whitespace-style' has `mark' and `color' as elements -and `whitespace-chars' has `newline' as an element." +Used when `whitespace-style' includes the values `newline-mark' +and `newline'." :type 'face :group 'whitespace) (defface whitespace-newline '((((class color) (background dark)) - (:background "grey26" :foreground "aquamarine3" :bold t)) + (:foreground "darkgray" :bold nil)) (((class color) (background light)) - (:background "linen" :foreground "aquamarine3" :bold t)) - (t (:bold t :underline t))) + (:foreground "lightgray" :bold nil)) + (t (:underline t :bold nil))) "Face used to visualize NEWLINE char mapping. See `whitespace-display-mappings'." @@ -503,9 +585,9 @@ See `whitespace-display-mappings'." (defcustom whitespace-trailing 'whitespace-trailing - "*Symbol face used to visualize traling blanks. + "*Symbol face used to visualize trailing blanks. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `trailing'." :type 'face :group 'whitespace) @@ -522,7 +604,7 @@ Used when `whitespace-style' has `color' as an element." See `whitespace-line-column'. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `line'." :type 'face :group 'whitespace) @@ -539,7 +621,7 @@ See `whitespace-line-column'." (defcustom whitespace-space-before-tab 'whitespace-space-before-tab "*Symbol face used to visualize SPACEs before TAB. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `space-before-tab'." :type 'face :group 'whitespace) @@ -554,7 +636,7 @@ Used when `whitespace-style' has `color' as an element." (defcustom whitespace-indentation 'whitespace-indentation "*Symbol face used to visualize 8 or more SPACEs at beginning of line. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `indentation'." :type 'face :group 'whitespace) @@ -569,7 +651,7 @@ Used when `whitespace-style' has `color' as an element." (defcustom whitespace-empty 'whitespace-empty "*Symbol face used to visualize empty lines at beginning and/or end of buffer. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `empty'." :type 'face :group 'whitespace) @@ -584,7 +666,7 @@ Used when `whitespace-style' has `color' as an element." (defcustom whitespace-space-after-tab 'whitespace-space-after-tab "*Symbol face used to visualize 8 or more SPACEs after TAB. -Used when `whitespace-style' has `color' as an element." +Used when `whitespace-style' includes the value `space-after-tab'." :type 'face :group 'whitespace) @@ -600,7 +682,7 @@ Used when `whitespace-style' has `color' as an element." "\\(\\(\xA0\\|\x8A0\\|\x920\\|\xE20\\|\xF20\\)+\\)" "*Specify HARD SPACE characters regexp. -If you're using `mule' package, it may exist other characters besides: +If you're using `mule' package, there may be other characters besides: \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \"\\xF20\" @@ -620,8 +702,7 @@ visualize only HARD SPACEs between TABs. NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `spaces' as an element." +Used when `whitespace-style' includes `spaces'." :type '(regexp :tag "HARD SPACE Chars") :group 'whitespace) @@ -629,7 +710,7 @@ Used when `whitespace-style' has `color' as an element, and (defcustom whitespace-space-regexp "\\( +\\)" "*Specify SPACE characters regexp. -If you're using `mule' package, it may exist other characters +If you're using `mule' package, there may be other characters besides \" \" that should be considered SPACE. Here are some examples: @@ -643,8 +724,7 @@ visualize leading and/or trailing SPACEs. NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `spaces' as an element." +Used when `whitespace-style' includes `spaces'." :type '(regexp :tag "SPACE Chars") :group 'whitespace) @@ -652,7 +732,7 @@ Used when `whitespace-style' has `color' as an element, and (defcustom whitespace-tab-regexp "\\(\t+\\)" "*Specify TAB characters regexp. -If you're using `mule' package, it may exist other characters +If you're using `mule' package, there may be other characters besides \"\\t\" that should be considered TAB. Here are some examples: @@ -666,78 +746,79 @@ visualize leading and/or trailing TABs. NOTE: Enclose always by \\\\( and \\\\) the elements to highlight. Use exactly one pair of enclosing \\\\( and \\\\). -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `tabs' as an element." +Used when `whitespace-style' includes `tabs'." :type '(regexp :tag "TAB Chars") :group 'whitespace) (defcustom whitespace-trailing-regexp - "\t\\| \\|\xA0\\|\x8A0\\|\x920\\|\xE20\\|\xF20" + "\\(\\(\t\\| \\|\xA0\\|\x8A0\\|\x920\\|\xE20\\|\xF20\\)+\\)$" "*Specify trailing characters regexp. -If you're using `mule' package, it may exist other characters besides: +If you're using `mule' package, there may be other characters besides: \" \" \"\\t\" \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \ \"\\xF20\" that should be considered blank. -NOTE: DO NOT enclose by \\\\( and \\\\) the elements to highlight. - `whitespace-mode' surrounds this regexp by \"\\\\(\\\\(\" and - \"\\\\)+\\\\)$\". +NOTE: Enclose always by \"\\\\(\" and \"\\\\)$\" the elements to highlight. + Use exactly one pair of enclosing elements above. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `trailing' as an element." +Used when `whitespace-style' includes `trailing'." :type '(regexp :tag "Trailing Chars") :group 'whitespace) -(defcustom whitespace-space-before-tab-regexp "\\( +\\)\t" +(defcustom whitespace-space-before-tab-regexp "\\( +\\)\\(\t+\\)" "*Specify SPACEs before TAB regexp. -If you're using `mule' package, it may exist other characters besides: +If you're using `mule' package, there may be other characters besides: \" \" \"\\t\" \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \ \"\\xF20\" that should be considered blank. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `space-before-tab' as an element." +Used when `whitespace-style' includes `space-before-tab', +`space-before-tab::tab' or `space-before-tab::space'." :type '(regexp :tag "SPACEs Before TAB") :group 'whitespace) (defcustom whitespace-indentation-regexp - "^\t*\\(\\( \\{8\\}\\)+\\)[^\n\t]" + '("^\t*\\(\\( \\{%d\\}\\)+\\)[^\n\t]" + . "^ *\\(\t+\\)[^\n]") "*Specify regexp for 8 or more SPACEs at beginning of line. -If you're using `mule' package, it may exist other characters besides: +It is a cons where the cons car is used for SPACEs visualization +and the cons cdr is used for TABs visualization. + +If you're using `mule' package, there may be other characters besides: \" \" \"\\t\" \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \ \"\\xF20\" that should be considered blank. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `indentation' as an element." - :type '(regexp :tag "Indentation SPACEs") +Used when `whitespace-style' includes `indentation', +`indentation::tab' or `indentation::space'." + :type '(cons (regexp :tag "Indentation SPACEs") + (regexp :tag "Indentation TABs")) :group 'whitespace) (defcustom whitespace-empty-at-bob-regexp "\\`\\(\\([ \t]*\n\\)+\\)" "*Specify regexp for empty lines at beginning of buffer. -If you're using `mule' package, it may exist other characters besides: +If you're using `mule' package, there may be other characters besides: \" \" \"\\t\" \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \ \"\\xF20\" that should be considered blank. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `empty' as an element." +Used when `whitespace-style' includes `empty'." :type '(regexp :tag "Empty Lines At Beginning Of Buffer") :group 'whitespace) @@ -745,31 +826,35 @@ Used when `whitespace-style' has `color' as an element, and (defcustom whitespace-empty-at-eob-regexp "^\\([ \t\n]+\\)\\'" "*Specify regexp for empty lines at end of buffer. -If you're using `mule' package, it may exist other characters besides: +If you're using `mule' package, there may be other characters besides: \" \" \"\\t\" \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \ \"\\xF20\" that should be considered blank. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `empty' as an element." +Used when `whitespace-style' includes `empty'." :type '(regexp :tag "Empty Lines At End Of Buffer") :group 'whitespace) -(defcustom whitespace-space-after-tab-regexp "\t\\(\\( \\{8\\}\\)+\\)" +(defcustom whitespace-space-after-tab-regexp + '("\t+\\(\\( \\{%d\\}\\)+\\)" + . "\\(\t+\\) +") "*Specify regexp for 8 or more SPACEs after TAB. -If you're using `mule' package, it may exist other characters besides: +It is a cons where the cons car is used for SPACEs visualization +and the cons cdr is used for TABs visualization. + +If you're using `mule' package, there may be other characters besides: \" \" \"\\t\" \"\\xA0\" \"\\x8A0\" \"\\x920\" \"\\xE20\" \ \"\\xF20\" that should be considered blank. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `space-after-tab' as an element." +Used when `whitespace-style' includes `space-after-tab', +`space-after-tab::tab' or `space-after-tab::space'." :type '(regexp :tag "SPACEs After TAB") :group 'whitespace) @@ -777,47 +862,52 @@ Used when `whitespace-style' has `color' as an element, and (defcustom whitespace-line-column 80 "*Specify column beyond which the line is highlighted. -Used when `whitespace-style' has `color' as an element, and -`whitespace-chars' has `lines' or `lines-tail' as an element." +Used when `whitespace-style' includes `lines' or `lines-tail'." :type '(integer :tag "Line Length") :group 'whitespace) ;; Hacked from `visible-whitespace-mappings' in visws.el (defcustom whitespace-display-mappings - ;; Due to limitations of glyph representation, the char code can not - ;; be above ?\x1FFFF. Probably, this will be fixed after Emacs - ;; unicode merging. '( - (?\ [?\xB7] [?.]) ; space - centered dot - (?\xA0 [?\xA4] [?_]) ; hard space - currency - (?\x8A0 [?\x8A4] [?_]) ; hard space - currency - (?\x920 [?\x924] [?_]) ; hard space - currency - (?\xE20 [?\xE24] [?_]) ; hard space - currency - (?\xF20 [?\xF24] [?_]) ; hard space - currency + (space-mark ?\ [?\xB7] [?.]) ; space - centered dot + (space-mark ?\xA0 [?\xA4] [?_]) ; hard space - currency + (space-mark ?\x8A0 [?\x8A4] [?_]) ; hard space - currency + (space-mark ?\x920 [?\x924] [?_]) ; hard space - currency + (space-mark ?\xE20 [?\xE24] [?_]) ; hard space - currency + (space-mark ?\xF20 [?\xF24] [?_]) ; hard space - currency ;; NEWLINE is displayed using the face `whitespace-newline' - (?\n [?$ ?\n]) ; end-of-line - dollar sign - ;; (?\n [?\u21B5 ?\n] [?$ ?\n]) ; end-of-line - downwards arrow - ;; (?\n [?\xB6 ?\n] [?$ ?\n]) ; end-of-line - pilcrow - ;; (?\n [?\x8AF ?\n] [?$ ?\n]) ; end-of-line - overscore - ;; (?\n [?\x8AC ?\n] [?$ ?\n]) ; end-of-line - negation - ;; (?\n [?\x8B0 ?\n] [?$ ?\n]) ; end-of-line - grade + (newline-mark ?\n [?$ ?\n]) ; eol - dollar sign + ;; (newline-mark ?\n [?\u21B5 ?\n] [?$ ?\n]) ; eol - downwards arrow + ;; (newline-mark ?\n [?\xB6 ?\n] [?$ ?\n]) ; eol - pilcrow + ;; (newline-mark ?\n [?\x8AF ?\n] [?$ ?\n]) ; eol - overscore + ;; (newline-mark ?\n [?\x8AC ?\n] [?$ ?\n]) ; eol - negation + ;; (newline-mark ?\n [?\x8B0 ?\n] [?$ ?\n]) ; eol - grade ;; ;; WARNING: the mapping below has a problem. ;; When a TAB occupies exactly one column, it will display the ;; character ?\xBB at that column followed by a TAB which goes to ;; the next TAB column. ;; If this is a problem for you, please, comment the line below. - (?\t [?\xBB ?\t] [?\\ ?\t]) ; tab - left quote mark + (tab-mark ?\t [?\xBB ?\t] [?\\ ?\t]) ; tab - left quote mark ) "*Specify an alist of mappings for displaying characters. Each element has the following form: - (CHAR VECTOR...) + (KIND CHAR VECTOR...) Where: +KIND is the kind of character. + It can be one of the following symbols: + + tab-mark for TAB character + + space-mark for SPACE or HARD SPACE character + + newline-mark for NEWLINE character + CHAR is the character to be mapped. VECTOR is a vector of characters to be displayed in place of CHAR. @@ -826,13 +916,16 @@ VECTOR is a vector of characters to be displayed in place of CHAR. that character is displayed unmodified. The NEWLINE character is displayed using the face given by -`whitespace-newline' variable. The characters in the vector to -be displayed will not have this face applied if the character -code is above #x1FFFF. +`whitespace-newline' variable. -Used when `whitespace-style' has `mark' as an element." +Used when `whitespace-style' includes `tab-mark', `space-mark' or +`newline-mark'." :type '(repeat (list :tag "Character Mapping" + (choice :tag "Char Kind" + (const :tag "Tab" tab-mark) + (const :tag "Space" space-mark) + (const :tag "Newline" newline-mark)) (character :tag "Char") (repeat :inline t :tag "Vector List" (vector :tag "" @@ -862,7 +955,8 @@ of the list is negated if it begins with `not'. For example: means that `whitespace-mode' is turned on for buffers in C and C++ modes only." - :type '(choice (const :tag "None" nil) + :type '(choice :tag "Global Modes" + (const :tag "None" nil) (const :tag "All" t) (set :menu-tag "Mode Specific" :tag "Modes" :value (not) @@ -871,6 +965,46 @@ C++ modes only." (symbol :tag "Mode")))) :group 'whitespace) + +(defcustom whitespace-action nil + "*Specify which action is taken when a buffer is visited or written. + +It's a list containing some or all of the following values: + + nil no action is taken. + + cleanup cleanup any bogus whitespace always when local + whitespace is turned on. + See `whitespace-cleanup' and + `whitespace-cleanup-region'. + + report-on-bogus report if there is any bogus whitespace always + when local whitespace is turned on. + + auto-cleanup cleanup any bogus whitespace when buffer is + written. + See `whitespace-cleanup' and + `whitespace-cleanup-region'. + + abort-on-bogus abort if there is any bogus whitespace and the + buffer is written. + + warn-if-read-only give a warning if `cleanup' or `auto-cleanup' + is included in `whitespace-action' and the + buffer is read-only. + +Any other value is treated as nil." + :type '(choice :tag "Actions" + (const :tag "None" nil) + (repeat :tag "Action List" + (choice :tag "Action" + (const :tag "Cleanup When On" cleanup) + (const :tag "Report On Bogus" report-on-bogus) + (const :tag "Auto Cleanup" auto-cleanup) + (const :tag "Abort On Bogus" abort-on-bogus) + (const :tag "Warn If Read-Only" warn-if-read-only)))) + :group 'whitespace) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; User commands - Local mode @@ -883,7 +1017,10 @@ C++ modes only." If ARG is null, toggle whitespace visualization. If ARG is a number greater than zero, turn on visualization; otherwise, turn off visualization. -Only useful with a windowing system." +Only useful with a windowing system. + +See also `whitespace-style', `whitespace-newline' and +`whitespace-display-mappings'." :lighter " ws" :init-value nil :global nil @@ -892,23 +1029,53 @@ Only useful with a windowing system." (noninteractive ; running a batch job (setq whitespace-mode nil)) (whitespace-mode ; whitespace-mode on - (whitespace-turn-on)) + (whitespace-turn-on) + (whitespace-action-when-on)) (t ; whitespace-mode off (whitespace-turn-off)))) + +;;;###autoload +(define-minor-mode whitespace-newline-mode + "Toggle NEWLINE minor mode visualization (\"nl\" on modeline). + +If ARG is null, toggle NEWLINE visualization. +If ARG is a number greater than zero, turn on visualization; +otherwise, turn off visualization. +Only useful with a windowing system. + +Use `whitespace-newline-mode' only for NEWLINE visualization +exclusively. For other visualizations, including NEWLINE +visualization together with (HARD) SPACEs and/or TABs, please, +use `whitespace-mode'. + +See also `whitespace-newline' and `whitespace-display-mappings'." + :lighter " nl" + :init-value nil + :global nil + :group 'whitespace + (let ((whitespace-style '(newline-mark newline))) + (whitespace-mode whitespace-newline-mode) + ;; sync states (running a batch job) + (setq whitespace-newline-mode whitespace-mode))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; User commands - Global mode +;;;###autoload (define-minor-mode global-whitespace-mode "Toggle whitespace global minor mode visualization (\"WS\" on modeline). If ARG is null, toggle whitespace visualization. If ARG is a number greater than zero, turn on visualization; otherwise, turn off visualization. -Only useful with a windowing system." - :lighter " BL" +Only useful with a windowing system. + +See also `whitespace-style', `whitespace-newline' and +`whitespace-display-mappings'." + :lighter " WS" :init-value nil :global t :group 'whitespace @@ -917,18 +1084,14 @@ Only useful with a windowing system." (setq global-whitespace-mode nil)) (global-whitespace-mode ; global-whitespace-mode on (save-excursion - (if (boundp 'find-file-hook) - (add-hook 'find-file-hook 'whitespace-turn-on-if-enabled t) - (add-hook 'find-file-hooks 'whitespace-turn-on-if-enabled t)) + (add-hook 'find-file-hook 'whitespace-turn-on-if-enabled) (dolist (buffer (buffer-list)) ; adjust all local mode (set-buffer buffer) (unless whitespace-mode (whitespace-turn-on-if-enabled))))) (t ; global-whitespace-mode off (save-excursion - (if (boundp 'find-file-hook) - (remove-hook 'find-file-hook 'whitespace-turn-on-if-enabled) - (remove-hook 'find-file-hooks 'whitespace-turn-on-if-enabled)) + (remove-hook 'find-file-hook 'whitespace-turn-on-if-enabled) (dolist (buffer (buffer-list)) ; adjust all local mode (set-buffer buffer) (unless whitespace-mode @@ -957,48 +1120,82 @@ Only useful with a windowing system." ;; Otherwise, turn on whitespace mode. (whitespace-turn-on))))) + +;;;###autoload +(define-minor-mode global-whitespace-newline-mode + "Toggle NEWLINE global minor mode visualization (\"NL\" on modeline). + +If ARG is null, toggle NEWLINE visualization. +If ARG is a number greater than zero, turn on visualization; +otherwise, turn off visualization. +Only useful with a windowing system. + +Use `global-whitespace-newline-mode' only for NEWLINE +visualization exclusively. For other visualizations, including +NEWLINE visualization together with (HARD) SPACEs and/or TABs, +please, use `global-whitespace-mode'. + +See also `whitespace-newline' and `whitespace-display-mappings'." + :lighter " NL" + :init-value nil + :global t + :group 'whitespace + (let ((whitespace-style '(newline-mark newline))) + (global-whitespace-mode global-whitespace-newline-mode) + ;; sync states (running a batch job) + (setq global-whitespace-newline-mode global-whitespace-mode))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; User commands - Toggle -(defconst whitespace-chars-value-list +(defconst whitespace-style-value-list '(tabs spaces trailing - space-before-tab lines lines-tail newline - indentation empty + indentation + indentation::tab + indentation::space space-after-tab - ) - "List of valid `whitespace-chars' values.") - - -(defconst whitespace-style-value-list - '(color - mark + space-after-tab::tab + space-after-tab::space + space-before-tab + space-before-tab::tab + space-before-tab::space + help-newline ; value used by `whitespace-insert-option-mark' + tab-mark + space-mark + newline-mark ) "List of valid `whitespace-style' values.") (defconst whitespace-toggle-option-alist - '((?t . tabs) - (?s . spaces) - (?r . trailing) - (?b . space-before-tab) - (?l . lines) - (?L . lines-tail) - (?n . newline) - (?i . indentation) - (?e . empty) - (?a . space-after-tab) - (?c . color) - (?m . mark) - (?x . whitespace-chars) - (?z . whitespace-style) + '((?t . tabs) + (?s . spaces) + (?r . trailing) + (?l . lines) + (?L . lines-tail) + (?n . newline) + (?e . empty) + (?\C-i . indentation) + (?I . indentation::tab) + (?i . indentation::space) + (?\C-a . space-after-tab) + (?A . space-after-tab::tab) + (?a . space-after-tab::space) + (?\C-b . space-before-tab) + (?B . space-before-tab::tab) + (?b . space-before-tab::space) + (?T . tab-mark) + (?S . space-mark) + (?N . newline-mark) + (?x . whitespace-style) ) "Alist of toggle options. @@ -1011,17 +1208,17 @@ Where: CHAR is a char which the user will have to type. SYMBOL is a valid symbol associated with CHAR. - See `whitespace-chars-value-list' and - `whitespace-style-value-list'.") + See `whitespace-style-value-list'.") -(defvar whitespace-active-chars nil - "Used to save locally `whitespace-chars' value.") -(make-variable-buffer-local 'whitespace-active-chars) - (defvar whitespace-active-style nil "Used to save locally `whitespace-style' value.") -(make-variable-buffer-local 'whitespace-active-style) + +(defvar whitespace-indent-tabs-mode indent-tabs-mode + "Used to save locally `indent-tabs-mode' value.") + +(defvar whitespace-tab-width tab-width + "Used to save locally `tab-width' value.") ;;;###autoload @@ -1037,20 +1234,30 @@ and restart local whitespace-mode. Interactively, it reads one of the following chars: CHAR MEANING + (VIA FACES) t toggle TAB visualization s toggle SPACE and HARD SPACE visualization r toggle trailing blanks visualization - b toggle SPACEs before TAB visualization l toggle \"long lines\" visualization L toggle \"long lines\" tail visualization n toggle NEWLINE visualization - i toggle indentation SPACEs visualization e toggle empty line at bob and/or eob visualization - a toggle SPACEs after TAB visualization - c toggle color faces - m toggle visual mark - x restore `whitespace-chars' value - z restore `whitespace-style' value + C-i toggle indentation SPACEs visualization (via `indent-tabs-mode') + I toggle indentation SPACEs visualization + i toggle indentation TABs visualization + C-a toggle SPACEs after TAB visualization (via `indent-tabs-mode') + A toggle SPACEs after TAB: SPACEs visualization + a toggle SPACEs after TAB: TABs visualization + C-b toggle SPACEs before TAB visualization (via `indent-tabs-mode') + B toggle SPACEs before TAB: SPACEs visualization + b toggle SPACEs before TAB: TABs visualization + + (VIA DISPLAY TABLE) + T toggle TAB visualization + S toggle SPACEs before TAB visualization + N toggle NEWLINE visualization + + x restore `whitespace-style' value ? display brief help Non-interactively, ARG should be a symbol or a list of symbols. @@ -1059,34 +1266,36 @@ The valid symbols are: tabs toggle TAB visualization spaces toggle SPACE and HARD SPACE visualization trailing toggle trailing blanks visualization - space-before-tab toggle SPACEs before TAB visualization lines toggle \"long lines\" visualization lines-tail toggle \"long lines\" tail visualization newline toggle NEWLINE visualization - indentation toggle indentation SPACEs visualization empty toggle empty line at bob and/or eob visualization - space-after-tab toggle SPACEs after TAB visualization - color toggle color faces - mark toggle visual mark - whitespace-chars restore `whitespace-chars' value - whitespace-style restore `whitespace-style' value + indentation toggle indentation SPACEs visualization + indentation::tab toggle indentation SPACEs visualization + indentation::space toggle indentation TABs visualization + space-after-tab toggle SPACEs after TAB visualization + space-after-tab::tab toggle SPACEs after TAB: SPACEs visualization + space-after-tab::space toggle SPACEs after TAB: TABs visualization + space-before-tab toggle SPACEs before TAB visualization + space-before-tab::tab toggle SPACEs before TAB: SPACEs visualization + space-before-tab::space toggle SPACEs before TAB: TABs visualization + + tab-mark toggle TAB visualization + space-mark toggle SPACEs before TAB visualization + newline-mark toggle NEWLINE visualization + + whitespace-style restore `whitespace-style' value -Only useful with a windowing system." +Only useful with a windowing system. + +See `whitespace-style' and `indent-tabs-mode' for documentation." (interactive (whitespace-interactive-char t)) - (let ((whitespace-chars - (whitespace-toggle-list - t arg whitespace-active-chars whitespace-chars - 'whitespace-chars whitespace-chars-value-list)) - (whitespace-style - (whitespace-toggle-list - t arg whitespace-active-style whitespace-style - 'whitespace-style whitespace-style-value-list))) + (let ((whitespace-style + (whitespace-toggle-list t arg whitespace-active-style))) (whitespace-mode 0) (whitespace-mode 1))) -(defvar whitespace-toggle-chars nil - "Used to toggle the global `whitespace-chars' value.") (defvar whitespace-toggle-style nil "Used to toggle the global `whitespace-style' value.") @@ -1101,23 +1310,33 @@ and turn on global whitespace-mode. If global whitespace-mode is on, toggle the option given by ARG and restart global whitespace-mode. -Interactively, it reads one of the following chars: +Interactively, it accepts one of the following chars: CHAR MEANING + (VIA FACES) t toggle TAB visualization s toggle SPACE and HARD SPACE visualization r toggle trailing blanks visualization - b toggle SPACEs before TAB visualization l toggle \"long lines\" visualization L toggle \"long lines\" tail visualization n toggle NEWLINE visualization - i toggle indentation SPACEs visualization e toggle empty line at bob and/or eob visualization - a toggle SPACEs after TAB visualization - c toggle color faces - m toggle visual mark - x restore `whitespace-chars' value - z restore `whitespace-style' value + C-i toggle indentation SPACEs visualization (via `indent-tabs-mode') + I toggle indentation SPACEs visualization + i toggle indentation TABs visualization + C-a toggle SPACEs after TAB visualization (via `indent-tabs-mode') + A toggle SPACEs after TAB: SPACEs visualization + a toggle SPACEs after TAB: TABs visualization + C-b toggle SPACEs before TAB visualization (via `indent-tabs-mode') + B toggle SPACEs before TAB: SPACEs visualization + b toggle SPACEs before TAB: TABs visualization + + (VIA DISPLAY TABLE) + T toggle TAB visualization + S toggle SPACEs before TAB visualization + N toggle NEWLINE visualization + + x restore `whitespace-style' value ? display brief help Non-interactively, ARG should be a symbol or a list of symbols. @@ -1126,30 +1345,33 @@ The valid symbols are: tabs toggle TAB visualization spaces toggle SPACE and HARD SPACE visualization trailing toggle trailing blanks visualization - space-before-tab toggle SPACEs before TAB visualization lines toggle \"long lines\" visualization lines-tail toggle \"long lines\" tail visualization newline toggle NEWLINE visualization - indentation toggle indentation SPACEs visualization empty toggle empty line at bob and/or eob visualization - space-after-tab toggle SPACEs after TAB visualization - color toggle color faces - mark toggle visual mark - whitespace-chars restore `whitespace-chars' value - whitespace-style restore `whitespace-style' value + indentation toggle indentation SPACEs visualization + indentation::tab toggle indentation SPACEs visualization + indentation::space toggle indentation TABs visualization + space-after-tab toggle SPACEs after TAB visualization + space-after-tab::tab toggle SPACEs after TAB: SPACEs visualization + space-after-tab::space toggle SPACEs after TAB: TABs visualization + space-before-tab toggle SPACEs before TAB visualization + space-before-tab::tab toggle SPACEs before TAB: SPACEs visualization + space-before-tab::space toggle SPACEs before TAB: TABs visualization + + tab-mark toggle TAB visualization + space-mark toggle SPACEs before TAB visualization + newline-mark toggle NEWLINE visualization + + whitespace-style restore `whitespace-style' value + +Only useful with a windowing system. -Only useful with a windowing system." +See `whitespace-style' and `indent-tabs-mode' for documentation." (interactive (whitespace-interactive-char nil)) - (let ((whitespace-chars - (whitespace-toggle-list - nil arg whitespace-toggle-chars whitespace-chars - 'whitespace-chars whitespace-chars-value-list)) - (whitespace-style - (whitespace-toggle-list - nil arg whitespace-toggle-style whitespace-style - 'whitespace-style whitespace-style-value-list))) - (setq whitespace-toggle-chars whitespace-chars - whitespace-toggle-style whitespace-style) + (let ((whitespace-style + (whitespace-toggle-list nil arg whitespace-toggle-style))) + (setq whitespace-toggle-style whitespace-style) (global-whitespace-mode 0) (global-whitespace-mode 1))) @@ -1164,52 +1386,76 @@ Only useful with a windowing system." It usually applies to the whole buffer, but in transient mark mode when the mark is active, it applies to the region. It also -applies to the region when it is not in transiente mark mode, the -mark is active and it was pressed `C-u' just before calling -`whitespace-cleanup' interactively. +applies to the region when it is not in transient mark mode, the +mark is active and \\[universal-argument] was pressed just before +calling `whitespace-cleanup' interactively. See also `whitespace-cleanup-region'. -The problems, which are cleaned up, are: +The problems cleaned up are: 1. empty lines at beginning of buffer. 2. empty lines at end of buffer. - If `whitespace-chars' has `empty' as an element, remove all + If `whitespace-style' includes the value `empty', remove all empty lines at beginning and/or end of buffer. 3. 8 or more SPACEs at beginning of line. - If `whitespace-chars' has `indentation' as an element, replace - 8 or more SPACEs at beginning of line by TABs. + If `whitespace-style' includes the value `indentation': + replace 8 or more SPACEs at beginning of line by TABs, if + `indent-tabs-mode' is non-nil; otherwise, replace TABs by + SPACEs. + If `whitespace-style' includes the value `indentation::tab', + replace 8 or more SPACEs at beginning of line by TABs. + If `whitespace-style' includes the value `indentation::space', + replace TABs by SPACEs. 4. SPACEs before TAB. - If `whitespace-chars' has `space-before-tab' as an element, - replace SPACEs by TABs. + If `whitespace-style' includes the value `space-before-tab': + replace SPACEs by TABs, if `indent-tabs-mode' is non-nil; + otherwise, replace TABs by SPACEs. + If `whitespace-style' includes the value + `space-before-tab::tab', replace SPACEs by TABs. + If `whitespace-style' includes the value + `space-before-tab::space', replace TABs by SPACEs. 5. SPACEs or TABs at end of line. - If `whitespace-chars' has `trailing' as an element, remove all - SPACEs or TABs at end of line. + If `whitespace-style' includes the value `trailing', remove + all SPACEs or TABs at end of line. 6. 8 or more SPACEs after TAB. - If `whitespace-chars' has `space-after-tab' as an element, - replace SPACEs by TABs." - (interactive "@*") - (if (and (or transient-mark-mode - current-prefix-arg) - mark-active) - ;; region active - ;; problems 1 and 2 are not handled in region - ;; problem 3: 8 or more SPACEs at bol - ;; problem 4: SPACEs before TAB - ;; problem 5: SPACEs or TABs at eol - ;; problem 6: 8 or more SPACEs after TAB - (whitespace-cleanup-region (region-beginning) (region-end)) - ;; whole buffer + If `whitespace-style' includes the value `space-after-tab': + replace SPACEs by TABs, if `indent-tabs-mode' is non-nil; + otherwise, replace TABs by SPACEs. + If `whitespace-style' includes the value + `space-after-tab::tab', replace SPACEs by TABs. + If `whitespace-style' includes the value + `space-after-tab::space', replace TABs by SPACEs. + +See `whitespace-style', `indent-tabs-mode' and `tab-width' for +documentation." + (interactive "@") + (cond + ;; read-only buffer + (buffer-read-only + (whitespace-warn-read-only "cleanup")) + ;; region active + ((and (or transient-mark-mode + current-prefix-arg) + mark-active) + ;; PROBLEMs 1 and 2 are not handled in region + ;; PROBLEM 3: 8 or more SPACEs at bol + ;; PROBLEM 4: SPACEs before TAB + ;; PROBLEM 5: SPACEs or TABs at eol + ;; PROBLEM 6: 8 or more SPACEs after TAB + (whitespace-cleanup-region (region-beginning) (region-end))) + ;; whole buffer + (t (save-excursion (save-match-data - ;; problem 1: empty lines at bob - ;; problem 2: empty lines at eob - ;; action: remove all empty lines at bob and/or eob - (when (memq 'empty whitespace-chars) + ;; PROBLEM 1: empty lines at bob + ;; PROBLEM 2: empty lines at eob + ;; ACTION: remove all empty lines at bob and/or eob + (when (memq 'empty whitespace-style) (let (overwrite-mode) ; enforce no overwrite (goto-char (point-min)) (when (re-search-forward @@ -1218,135 +1464,397 @@ The problems, which are cleaned up, are: (when (re-search-forward whitespace-empty-at-eob-regexp nil t) (delete-region (match-beginning 1) (match-end 1))))))) - ;; problem 3: 8 or more SPACEs at bol - ;; problem 4: SPACEs before TAB - ;; problem 5: SPACEs or TABs at eol - ;; problem 6: 8 or more SPACEs after TAB - (whitespace-cleanup-region (point-min) (point-max)))) + ;; PROBLEM 3: 8 or more SPACEs at bol + ;; PROBLEM 4: SPACEs before TAB + ;; PROBLEM 5: SPACEs or TABs at eol + ;; PROBLEM 6: 8 or more SPACEs after TAB + (whitespace-cleanup-region (point-min) (point-max))))) ;;;###autoload (defun whitespace-cleanup-region (start end) "Cleanup some blank problems at region. -The problems, which are cleaned up, are: +The problems cleaned up are: 1. 8 or more SPACEs at beginning of line. - If `whitespace-chars' has `indentation' as an element, replace - 8 or more SPACEs at beginning of line by TABs. + If `whitespace-style' includes the value `indentation': + replace 8 or more SPACEs at beginning of line by TABs, if + `indent-tabs-mode' is non-nil; otherwise, replace TABs by + SPACEs. + If `whitespace-style' includes the value `indentation::tab', + replace 8 or more SPACEs at beginning of line by TABs. + If `whitespace-style' includes the value `indentation::space', + replace TABs by SPACEs. 2. SPACEs before TAB. - If `whitespace-chars' has `space-before-tab' as an element, - replace SPACEs by TABs. + If `whitespace-style' includes the value `space-before-tab': + replace SPACEs by TABs, if `indent-tabs-mode' is non-nil; + otherwise, replace TABs by SPACEs. + If `whitespace-style' includes the value + `space-before-tab::tab', replace SPACEs by TABs. + If `whitespace-style' includes the value + `space-before-tab::space', replace TABs by SPACEs. 3. SPACEs or TABs at end of line. - If `whitespace-chars' has `trailing' as an element, remove all - SPACEs or TABs at end of line. + If `whitespace-style' includes the value `trailing', remove + all SPACEs or TABs at end of line. 4. 8 or more SPACEs after TAB. - If `whitespace-chars' has `space-after-tab' as an element, - replace SPACEs by TABs." - (interactive "@*r") - (let ((rstart (min start end)) - (rend (copy-marker (max start end))) - (tab-width 8) ; assure TAB width - (indent-tabs-mode t) ; always insert TABs - overwrite-mode ; enforce no overwrite - tmp) - (save-excursion - (save-match-data - ;; problem 1: 8 or more SPACEs at bol - ;; action: replace 8 or more SPACEs at bol by TABs - (when (memq 'indentation whitespace-chars) - (goto-char rstart) - (while (re-search-forward - whitespace-indentation-regexp rend t) - (setq tmp (current-indentation)) - (delete-horizontal-space) - (unless (eolp) - (indent-to tmp)))) - ;; problem 3: SPACEs or TABs at eol - ;; action: remove all SPACEs or TABs at eol - (when (memq 'trailing whitespace-chars) - (let ((regexp (concat "\\(\\(" whitespace-trailing-regexp - "\\)+\\)$"))) - (goto-char rstart) - (while (re-search-forward regexp rend t) - (delete-region (match-beginning 1) (match-end 1))))) - ;; problem 4: 8 or more SPACEs after TAB - ;; action: replace 8 or more SPACEs by TABs - (when (memq 'space-after-tab whitespace-chars) - (whitespace-replace-spaces-by-tabs - rstart rend whitespace-space-after-tab-regexp)) - ;; problem 2: SPACEs before TAB - ;; action: replace SPACEs before TAB by TABs - (when (memq 'space-before-tab whitespace-chars) - (whitespace-replace-spaces-by-tabs - rstart rend whitespace-space-before-tab-regexp)))) - (set-marker rend nil))) ; point marker to nowhere - - -(defun whitespace-replace-spaces-by-tabs (rstart rend regexp) - "Replace all SPACEs by TABs matched by REGEXP between RSTART and REND." + If `whitespace-style' includes the value `space-after-tab': + replace SPACEs by TABs, if `indent-tabs-mode' is non-nil; + otherwise, replace TABs by SPACEs. + If `whitespace-style' includes the value + `space-after-tab::tab', replace SPACEs by TABs. + If `whitespace-style' includes the value + `space-after-tab::space', replace TABs by SPACEs. + +See `whitespace-style', `indent-tabs-mode' and `tab-width' for +documentation." + (interactive "@r") + (if buffer-read-only + ;; read-only buffer + (whitespace-warn-read-only "cleanup region") + ;; non-read-only buffer + (let ((rstart (min start end)) + (rend (copy-marker (max start end))) + (indent-tabs-mode whitespace-indent-tabs-mode) + (tab-width whitespace-tab-width) + overwrite-mode ; enforce no overwrite + tmp) + (save-excursion + (save-match-data + ;; PROBLEM 1: 8 or more SPACEs at bol + (cond + ;; ACTION: replace 8 or more SPACEs at bol by TABs, if + ;; `indent-tabs-mode' is non-nil; otherwise, replace TABs + ;; by SPACEs. + ((memq 'indentation whitespace-style) + (let ((regexp (whitespace-indentation-regexp))) + (goto-char rstart) + (while (re-search-forward regexp rend t) + (setq tmp (current-indentation)) + (goto-char (match-beginning 0)) + (delete-horizontal-space) + (unless (eolp) + (indent-to tmp))))) + ;; ACTION: replace 8 or more SPACEs at bol by TABs. + ((memq 'indentation::tab whitespace-style) + (whitespace-replace-action + 'tabify rstart rend + (whitespace-indentation-regexp 'tab) 0)) + ;; ACTION: replace TABs by SPACEs. + ((memq 'indentation::space whitespace-style) + (whitespace-replace-action + 'untabify rstart rend + (whitespace-indentation-regexp 'space) 0))) + ;; PROBLEM 3: SPACEs or TABs at eol + ;; ACTION: remove all SPACEs or TABs at eol + (when (memq 'trailing whitespace-style) + (whitespace-replace-action + 'delete-region rstart rend + whitespace-trailing-regexp 1)) + ;; PROBLEM 4: 8 or more SPACEs after TAB + (cond + ;; ACTION: replace 8 or more SPACEs by TABs, if + ;; `indent-tabs-mode' is non-nil; otherwise, replace TABs + ;; by SPACEs. + ((memq 'space-after-tab whitespace-style) + (whitespace-replace-action + (if whitespace-indent-tabs-mode 'tabify 'untabify) + rstart rend (whitespace-space-after-tab-regexp) 1)) + ;; ACTION: replace 8 or more SPACEs by TABs. + ((memq 'space-after-tab::tab whitespace-style) + (whitespace-replace-action + 'tabify rstart rend + (whitespace-space-after-tab-regexp 'tab) 1)) + ;; ACTION: replace TABs by SPACEs. + ((memq 'space-after-tab::space whitespace-style) + (whitespace-replace-action + 'untabify rstart rend + (whitespace-space-after-tab-regexp 'space) 1))) + ;; PROBLEM 2: SPACEs before TAB + (cond + ;; ACTION: replace SPACEs before TAB by TABs, if + ;; `indent-tabs-mode' is non-nil; otherwise, replace TABs + ;; by SPACEs. + ((memq 'space-before-tab whitespace-style) + (whitespace-replace-action + (if whitespace-indent-tabs-mode 'tabify 'untabify) + rstart rend whitespace-space-before-tab-regexp + (if whitespace-indent-tabs-mode 1 2))) + ;; ACTION: replace SPACEs before TAB by TABs. + ((memq 'space-before-tab::tab whitespace-style) + (whitespace-replace-action + 'tabify rstart rend + whitespace-space-before-tab-regexp 1)) + ;; ACTION: replace TABs by SPACEs. + ((memq 'space-before-tab::space whitespace-style) + (whitespace-replace-action + 'untabify rstart rend + whitespace-space-before-tab-regexp 2))))) + (set-marker rend nil)))) ; point marker to nowhere + + +(defun whitespace-replace-action (action rstart rend regexp index) + "Do ACTION in the string matched by REGEXP between RSTART and REND. + +INDEX is the level group matched by REGEXP and used by ACTION. + +See also `tab-width'." (goto-char rstart) (while (re-search-forward regexp rend t) - (goto-char (match-beginning 1)) - (let* ((scol (current-column)) - (ecol (save-excursion - (goto-char (match-end 1)) - (current-column)))) - (delete-region (match-beginning 1) (match-end 1)) - (insert-char ?\t - (/ (- (- ecol (% ecol 8)) ; prev end col - (- scol (% scol 8))) ; prev start col - 8))))) + (goto-char (match-end index)) + (funcall action (match-beginning index) (match-end index)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; User command - old whitespace compatibility +;;;; User command - report + + +(defun whitespace-regexp (regexp &optional kind) + "Return REGEXP depending on `whitespace-indent-tabs-mode'." + (cond + ((or (eq kind 'tab) + whitespace-indent-tabs-mode) + (format (car regexp) whitespace-tab-width)) + ((or (eq kind 'space) + (not whitespace-indent-tabs-mode)) + (cdr regexp)))) + + +(defun whitespace-indentation-regexp (&optional kind) + "Return the indentation regexp depending on `whitespace-indent-tabs-mode'." + (whitespace-regexp whitespace-indentation-regexp kind)) + + +(defun whitespace-space-after-tab-regexp (&optional kind) + "Return the space-after-tab regexp depending on `whitespace-indent-tabs-mode'." + (whitespace-regexp whitespace-space-after-tab-regexp kind)) + + +(defconst whitespace-report-list + (list + (cons 'empty whitespace-empty-at-bob-regexp) + (cons 'empty whitespace-empty-at-eob-regexp) + (cons 'trailing whitespace-trailing-regexp) + (cons 'indentation nil) + (cons 'indentation::tab nil) + (cons 'indentation::space nil) + (cons 'space-before-tab whitespace-space-before-tab-regexp) + (cons 'space-before-tab::tab whitespace-space-before-tab-regexp) + (cons 'space-before-tab::space whitespace-space-before-tab-regexp) + (cons 'space-after-tab nil) + (cons 'space-after-tab::tab nil) + (cons 'space-after-tab::space nil) + ) + "List of whitespace bogus symbol and corresponding regexp.") + + +(defconst whitespace-report-text + '( ;; `indent-tabs-mode' has non-nil value + "\ + Whitespace Report + + Current Setting Whitespace Problem + + empty [] [] empty lines at beginning of buffer + empty [] [] empty lines at end of buffer + trailing [] [] SPACEs or TABs at end of line + indentation [] [] 8 or more SPACEs at beginning of line + indentation::tab [] [] 8 or more SPACEs at beginning of line + indentation::space [] [] TABs at beginning of line + space-before-tab [] [] SPACEs before TAB + space-before-tab::tab [] [] SPACEs before TAB: SPACEs + space-before-tab::space [] [] SPACEs before TAB: TABs + space-after-tab [] [] 8 or more SPACEs after TAB + space-after-tab::tab [] [] 8 or more SPACEs after TAB: SPACEs + space-after-tab::space [] [] 8 or more SPACEs after TAB: TABs + + indent-tabs-mode = + tab-width = \n\n" + . ;; `indent-tabs-mode' has nil value + "\ + Whitespace Report + + Current Setting Whitespace Problem + + empty [] [] empty lines at beginning of buffer + empty [] [] empty lines at end of buffer + trailing [] [] SPACEs or TABs at end of line + indentation [] [] TABs at beginning of line + indentation::tab [] [] 8 or more SPACEs at beginning of line + indentation::space [] [] TABs at beginning of line + space-before-tab [] [] SPACEs before TAB + space-before-tab::tab [] [] SPACEs before TAB: SPACEs + space-before-tab::space [] [] SPACEs before TAB: TABs + space-after-tab [] [] 8 or more SPACEs after TAB + space-after-tab::tab [] [] 8 or more SPACEs after TAB: SPACEs + space-after-tab::space [] [] 8 or more SPACEs after TAB: TABs + + indent-tabs-mode = + tab-width = \n\n") + "Text for whitespace bogus report. + +It is a cons of strings, where the car part is used when +`indent-tabs-mode' is non-nil, and the cdr part is used when +`indent-tabs-mode' is nil.") + + +(defconst whitespace-report-buffer-name "*Whitespace Report*" + "The buffer name for whitespace bogus report.") ;;;###autoload -(defun whitespace-buffer () - "Turn on `whitespace-mode' forcing some settings. +(defun whitespace-report (&optional force report-if-bogus) + "Report some whitespace problems in buffer. -It forces `whitespace-style' to have `color'. +Return nil if there is no whitespace problem; otherwise, return +non-nil. -It also forces `whitespace-chars' to have: +If FORCE is non-nil or \\[universal-argument] was pressed just +before calling `whitespace-report' interactively, it forces +`whitespace-style' to have: + empty trailing indentation space-before-tab + space-after-tab + +If REPORT-IF-BOGUS is non-nil, it reports only when there are any +whitespace problems in buffer. + +Report if some of the following whitespace problems exist: + +* If `indent-tabs-mode' is non-nil: + empty 1. empty lines at beginning of buffer. + empty 2. empty lines at end of buffer. + trailing 3. SPACEs or TABs at end of line. + indentation 4. 8 or more SPACEs at beginning of line. + space-before-tab 5. SPACEs before TAB. + space-after-tab 6. 8 or more SPACEs after TAB. + +* If `indent-tabs-mode' is nil: + empty 1. empty lines at beginning of buffer. + empty 2. empty lines at end of buffer. + trailing 3. SPACEs or TABs at end of line. + indentation 4. TABS at beginning of line. + space-before-tab 5. SPACEs before TAB. + space-after-tab 6. 8 or more SPACEs after TAB. + +See `whitespace-style' for documentation. +See also `whitespace-cleanup' and `whitespace-cleanup-region' for +cleaning up these problems." + (interactive (list current-prefix-arg)) + (whitespace-report-region (point-min) (point-max) + force report-if-bogus)) + + +;;;###autoload +(defun whitespace-report-region (start end &optional force report-if-bogus) + "Report some whitespace problems in a region. + +Return nil if there is no whitespace problem; otherwise, return +non-nil. + +If FORCE is non-nil or \\[universal-argument] was pressed just +before calling `whitespace-report-region' interactively, it +forces `whitespace-style' to have: + empty + indentation + space-before-tab + trailing space-after-tab -So, it is possible to visualize the following problems: +If REPORT-IF-BOGUS is non-nil, it reports only when there are any +whitespace problems in buffer. +Report if some of the following whitespace problems exist: + +* If `indent-tabs-mode' is non-nil: empty 1. empty lines at beginning of buffer. empty 2. empty lines at end of buffer. - indentation 3. 8 or more SPACEs at beginning of line. - space-before-tab 4. SPACEs before TAB. - trailing 5. SPACEs or TABs at end of line. + trailing 3. SPACEs or TABs at end of line. + indentation 4. 8 or more SPACEs at beginning of line. + space-before-tab 5. SPACEs before TAB. space-after-tab 6. 8 or more SPACEs after TAB. -See `whitespace-chars' and `whitespace-style' for documentation. +* If `indent-tabs-mode' is nil: + empty 1. empty lines at beginning of buffer. + empty 2. empty lines at end of buffer. + trailing 3. SPACEs or TABs at end of line. + indentation 4. TABS at beginning of line. + space-before-tab 5. SPACEs before TAB. + space-after-tab 6. 8 or more SPACEs after TAB. + +See `whitespace-style' for documentation. See also `whitespace-cleanup' and `whitespace-cleanup-region' for cleaning up these problems." - (interactive) - (whitespace-mode 0) ; assure is off - ;; keep original values - (let ((whitespace-style (copy-sequence whitespace-style)) - (whitespace-chars (copy-sequence whitespace-chars))) - ;; adjust options for whitespace bogus blanks - (add-to-list 'whitespace-style 'color) - (mapc #'(lambda (option) - (add-to-list 'whitespace-chars option)) - '(trailing - indentation - space-before-tab - empty - space-after-tab)) - (whitespace-mode 1))) ; turn on + (interactive "r") + (setq force (or current-prefix-arg force)) + (save-excursion + (save-match-data + (let* ((has-bogus nil) + (rstart (min start end)) + (rend (max start end)) + (bogus-list + (mapcar + #'(lambda (option) + (when force + (add-to-list 'whitespace-style (car option))) + (goto-char rstart) + (let ((regexp + (cond + ((eq (car option) 'indentation) + (whitespace-indentation-regexp)) + ((eq (car option) 'indentation::tab) + (whitespace-indentation-regexp 'tab)) + ((eq (car option) 'indentation::space) + (whitespace-indentation-regexp 'space)) + ((eq (car option) 'space-after-tab) + (whitespace-space-after-tab-regexp)) + ((eq (car option) 'space-after-tab::tab) + (whitespace-space-after-tab-regexp 'tab)) + ((eq (car option) 'space-after-tab::space) + (whitespace-space-after-tab-regexp 'space)) + (t + (cdr option))))) + (and (re-search-forward regexp rend t) + (setq has-bogus t)))) + whitespace-report-list))) + (when (if report-if-bogus has-bogus t) + (whitespace-kill-buffer whitespace-report-buffer-name) + ;; `whitespace-indent-tabs-mode' is local to current buffer + ;; `whitespace-tab-width' is local to current buffer + (let ((ws-indent-tabs-mode whitespace-indent-tabs-mode) + (ws-tab-width whitespace-tab-width)) + (with-current-buffer (get-buffer-create + whitespace-report-buffer-name) + (erase-buffer) + (insert (if ws-indent-tabs-mode + (car whitespace-report-text) + (cdr whitespace-report-text))) + (goto-char (point-min)) + (forward-line 3) + (dolist (option whitespace-report-list) + (forward-line 1) + (whitespace-mark-x + 27 (memq (car option) whitespace-style)) + (whitespace-mark-x 7 (car bogus-list)) + (setq bogus-list (cdr bogus-list))) + (forward-line 1) + (whitespace-insert-value ws-indent-tabs-mode) + (whitespace-insert-value ws-tab-width) + (when has-bogus + (goto-char (point-max)) + (insert " Type `M-x whitespace-cleanup'" + " to cleanup the buffer.\n\n" + " Type `M-x whitespace-cleanup-region'" + " to cleanup a region.\n\n")) + (whitespace-display-window (current-buffer))))) + has-bogus)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1355,37 +1863,42 @@ cleaning up these problems." (defvar whitespace-font-lock-mode nil "Used to remember whether a buffer had font lock mode on or not.") -(make-variable-buffer-local 'whitespace-font-lock-mode) (defvar whitespace-font-lock nil "Used to remember whether a buffer initially had font lock on or not.") -(make-variable-buffer-local 'whitespace-font-lock) (defvar whitespace-font-lock-keywords nil "Used to save locally `font-lock-keywords' value.") -(make-variable-buffer-local 'whitespace-font-lock-keywords) (defconst whitespace-help-text "\ - whitespace-mode toggle options: - - [] t - toggle TAB visualization - [] s - toggle SPACE and HARD SPACE visualization - [] r - toggle trailing blanks visualization - [] b - toggle SPACEs before TAB visualization - [] l - toggle \"long lines\" visualization - [] L - toggle \"long lines\" tail visualization - [] n - toggle NEWLINE visualization - [] i - toggle indentation SPACEs visualization - [] e - toggle empty line at bob and/or eob visualization - [] a - toggle SPACEs after TAB visualization - - [] c - toggle color faces - [] m - toggle visual mark - - x - restore `whitespace-chars' value - z - restore `whitespace-style' value + Whitespace Toggle Options + + FACES + [] t - toggle TAB visualization + [] s - toggle SPACE and HARD SPACE visualization + [] r - toggle trailing blanks visualization + [] l - toggle \"long lines\" visualization + [] L - toggle \"long lines\" tail visualization + [] n - toggle NEWLINE visualization + [] e - toggle empty line at bob and/or eob visualization + [] C-i - toggle indentation SPACEs visualization (via `indent-tabs-mode') + [] I - toggle indentation SPACEs visualization + [] i - toggle indentation TABs visualization + [] C-a - toggle SPACEs after TAB visualization (via `indent-tabs-mode') + [] A - toggle SPACEs after TAB: SPACEs visualization + [] a - toggle SPACEs after TAB: TABs visualization + [] C-b - toggle SPACEs before TAB visualization (via `indent-tabs-mode') + [] B - toggle SPACEs before TAB: SPACEs visualization + [] b - toggle SPACEs before TAB: TABs visualization + + DISPLAY TABLE + [] T - toggle TAB visualization + [] S - toggle SPACE and HARD SPACE visualization + [] N - toggle NEWLINE visualization + + x - restore `whitespace-style' value ? - display this text\n\n" "Text for whitespace toggle options.") @@ -1395,16 +1908,31 @@ cleaning up these problems." "The buffer name for whitespace toggle options.") +(defun whitespace-insert-value (value) + "Insert VALUE at column 20 of next line." + (forward-line 1) + (move-to-column 20 t) + (insert (format "%s" value))) + + +(defun whitespace-mark-x (nchars condition) + "Insert the mark ('X' or ' ') after NCHARS depending on CONDITION." + (forward-char nchars) + (insert (if condition "X" " "))) + + (defun whitespace-insert-option-mark (the-list the-value) "Insert the option mark ('X' or ' ') in toggle options buffer." - (forward-line 1) + (goto-char (point-min)) + (forward-line 2) (dolist (sym the-list) - (forward-line 1) - (forward-char 2) - (insert (if (memq sym the-value) "X" " ")))) + (if (eq sym 'help-newline) + (forward-line 2) + (forward-line 1) + (whitespace-mark-x 2 (memq sym the-value))))) -(defun whitespace-help-on (chars style) +(defun whitespace-help-on (style) "Display the whitespace toggle options." (unless (get-buffer whitespace-help-buffer-name) (delete-other-windows) @@ -1413,64 +1941,78 @@ cleaning up these problems." (set-buffer buffer) (erase-buffer) (insert whitespace-help-text) - (goto-char (point-min)) - (whitespace-insert-option-mark - whitespace-chars-value-list chars) (whitespace-insert-option-mark whitespace-style-value-list style) - (goto-char (point-min)) - (set-buffer-modified-p nil) - (let ((size (- (window-height) - (max window-min-height - (1+ (count-lines (point-min) - (point-max))))))) - (when (<= size 0) - (kill-buffer buffer) - (error "Frame height is too small; \ + (whitespace-display-window buffer))))) + + +(defun whitespace-display-window (buffer) + "Display BUFFER in a new window." + (goto-char (point-min)) + (set-buffer-modified-p nil) + (let ((size (- (window-height) + (max window-min-height + (1+ (count-lines (point-min) + (point-max))))))) + (when (<= size 0) + (kill-buffer buffer) + (error "Frame height is too small; \ can't split window to display whitespace toggle options")) - (set-window-buffer (split-window nil size) buffer)))))) + (set-window-buffer (split-window nil size) buffer))) -(defun whitespace-help-off () - "Remove the buffer and window of the whitespace toggle options." - (let ((buffer (get-buffer whitespace-help-buffer-name))) +(defun whitespace-kill-buffer (buffer-name) + "Kill buffer BUFFER-NAME and windows related with it." + (let ((buffer (get-buffer buffer-name))) (when buffer (delete-windows-on buffer) (kill-buffer buffer)))) +(defun whitespace-help-off () + "Remove the buffer and window of the whitespace toggle options." + (whitespace-kill-buffer whitespace-help-buffer-name)) + + (defun whitespace-interactive-char (local-p) "Interactive function to read a char and return a symbol. If LOCAL-P is non-nil, it uses a local context; otherwise, it uses a global context. -It reads one of the following chars: +It accepts one of the following chars: CHAR MEANING + (VIA FACES) t toggle TAB visualization s toggle SPACE and HARD SPACE visualization r toggle trailing blanks visualization - b toggle SPACEs before TAB visualization l toggle \"long lines\" visualization L toggle \"long lines\" tail visualization n toggle NEWLINE visualization - i toggle indentation SPACEs visualization e toggle empty line at bob and/or eob visualization - a toggle SPACEs after TAB visualization - c toggle color faces - m toggle visual mark - x restore `whitespace-chars' value - z restore `whitespace-style' value + C-i toggle indentation SPACEs visualization (via `indent-tabs-mode') + I toggle indentation SPACEs visualization + i toggle indentation TABs visualization + C-a toggle SPACEs after TAB visualization (via `indent-tabs-mode') + A toggle SPACEs after TAB: SPACEs visualization + a toggle SPACEs after TAB: TABs visualization + C-b toggle SPACEs before TAB visualization (via `indent-tabs-mode') + B toggle SPACEs before TAB: SPACEs visualization + b toggle SPACEs before TAB: TABs visualization + + (VIA DISPLAY TABLE) + T toggle TAB visualization + S toggle SPACE and HARD SPACE visualization + N toggle NEWLINE visualization + + x restore `whitespace-style' value ? display brief help See also `whitespace-toggle-option-alist'." (let* ((is-off (not (if local-p whitespace-mode global-whitespace-mode))) - (chars (cond (is-off whitespace-chars) ; use default value - (local-p whitespace-active-chars) - (t whitespace-toggle-chars))) (style (cond (is-off whitespace-style) ; use default value (local-p whitespace-active-style) (t whitespace-toggle-style))) @@ -1492,7 +2034,7 @@ See also `whitespace-toggle-option-alist'." (assq ch whitespace-toggle-option-alist))))) ;; while body (if (eq ch ?\?) - (whitespace-help-on chars style) + (whitespace-help-on style) (ding))) (whitespace-help-off) (message " ")) ; clean echo area @@ -1503,8 +2045,7 @@ See also `whitespace-toggle-option-alist'." (list sym))) ; return the apropriate symbol -(defun whitespace-toggle-list (local-p arg the-list default-list - sym-restore sym-list) +(defun whitespace-toggle-list (local-p arg the-list) "Toggle options in THE-LIST based on list ARG. If LOCAL-P is non-nil, it uses a local context; otherwise, it @@ -1513,66 +2054,97 @@ uses a global context. ARG is a list of options to be toggled. THE-LIST is a list of options. This list will be toggled and the -resultant list will be returned. - -DEFAULT-LIST is the default list of options. It is used to -restore the options in THE-LIST. - -SYM-RESTORE is the symbol which indicates to restore the options -in THE-LIST. - -SYM-LIST is a list of valid options, used to check if the ARG's -options are valid." +resultant list will be returned." (unless (if local-p whitespace-mode global-whitespace-mode) - (setq the-list default-list)) + (setq the-list whitespace-style)) (setq the-list (copy-sequence the-list)) ; keep original list (dolist (sym (if (listp arg) arg (list arg))) (cond + ;; ignore help value + ((eq sym 'help-newline)) ;; restore default values - ((eq sym sym-restore) - (setq the-list default-list)) + ((eq sym 'whitespace-style) + (setq the-list whitespace-style)) ;; toggle valid values - ((memq sym sym-list) + ((memq sym whitespace-style-value-list) (setq the-list (if (memq sym the-list) (delq sym the-list) (cons sym the-list)))))) the-list) +(defvar whitespace-display-table nil + "Used to save a local display table.") + +(defvar whitespace-display-table-was-local nil + "Used to remember whether a buffer initially had a local display table.") + + (defun whitespace-turn-on () "Turn on whitespace visualization." - (setq whitespace-active-style (if (listp whitespace-style) - whitespace-style - (list whitespace-style))) - (setq whitespace-active-chars (if (listp whitespace-chars) - whitespace-chars - (list whitespace-chars))) - (when (memq 'color whitespace-active-style) - (whitespace-color-on)) - (when (memq 'mark whitespace-active-style) + ;; prepare local hooks + (add-hook 'write-file-functions 'whitespace-write-file-hook nil t) + ;; create whitespace local buffer environment + (set (make-local-variable 'whitespace-font-lock-mode) nil) + (set (make-local-variable 'whitespace-font-lock) nil) + (set (make-local-variable 'whitespace-font-lock-keywords) nil) + (set (make-local-variable 'whitespace-display-table) nil) + (set (make-local-variable 'whitespace-display-table-was-local) nil) + (set (make-local-variable 'whitespace-active-style) + (if (listp whitespace-style) + whitespace-style + (list whitespace-style))) + (set (make-local-variable 'whitespace-indent-tabs-mode) + indent-tabs-mode) + (set (make-local-variable 'whitespace-tab-width) + tab-width) + ;; turn on whitespace + (when whitespace-active-style + (whitespace-color-on) (whitespace-display-char-on))) (defun whitespace-turn-off () - "Turn off whitesapce visualization." - (when (memq 'color whitespace-active-style) - (whitespace-color-off)) - (when (memq 'mark whitespace-active-style) + "Turn off whitespace visualization." + (remove-hook 'write-file-functions 'whitespace-write-file-hook t) + (when whitespace-active-style + (whitespace-color-off) (whitespace-display-char-off))) +(defun whitespace-style-face-p () + "Return t if there is some visualization via face." + (or (memq 'tabs whitespace-active-style) + (memq 'spaces whitespace-active-style) + (memq 'trailing whitespace-active-style) + (memq 'lines whitespace-active-style) + (memq 'lines-tail whitespace-active-style) + (memq 'newline whitespace-active-style) + (memq 'empty whitespace-active-style) + (memq 'indentation whitespace-active-style) + (memq 'indentation::tab whitespace-active-style) + (memq 'indentation::space whitespace-active-style) + (memq 'space-after-tab whitespace-active-style) + (memq 'space-after-tab::tab whitespace-active-style) + (memq 'space-after-tab::space whitespace-active-style) + (memq 'space-before-tab whitespace-active-style) + (memq 'space-before-tab::tab whitespace-active-style) + (memq 'space-before-tab::space whitespace-active-style))) + + (defun whitespace-color-on () "Turn on color visualization." - (when whitespace-active-chars + (when (whitespace-style-face-p) (unless whitespace-font-lock (setq whitespace-font-lock t whitespace-font-lock-keywords (copy-sequence font-lock-keywords))) ;; turn off font lock - (setq whitespace-font-lock-mode font-lock-mode) + (set (make-local-variable 'whitespace-font-lock-mode) + font-lock-mode) (font-lock-mode 0) ;; add whitespace-mode color into font lock - (when (memq 'spaces whitespace-active-chars) + (when (memq 'spaces whitespace-active-style) (font-lock-add-keywords nil (list @@ -1581,23 +2153,22 @@ options are valid." ;; Show HARD SPACEs (list whitespace-hspace-regexp 1 whitespace-hspace t)) t)) - (when (memq 'tabs whitespace-active-chars) + (when (memq 'tabs whitespace-active-style) (font-lock-add-keywords nil (list ;; Show TABs (list whitespace-tab-regexp 1 whitespace-tab t)) t)) - (when (memq 'trailing whitespace-active-chars) + (when (memq 'trailing whitespace-active-style) (font-lock-add-keywords nil (list ;; Show trailing blanks - (list (concat "\\(\\(" whitespace-trailing-regexp "\\)+\\)$") - 1 whitespace-trailing t)) + (list whitespace-trailing-regexp 1 whitespace-trailing t)) t)) - (when (or (memq 'lines whitespace-active-chars) - (memq 'lines-tail whitespace-active-chars)) + (when (or (memq 'lines whitespace-active-style) + (memq 'lines-tail whitespace-active-style)) (font-lock-add-keywords nil (list @@ -1605,34 +2176,69 @@ options are valid." (list (format "^\\([^\t\n]\\{%s\\}\\|[^\t\n]\\{0,%s\\}\t\\)\\{%d\\}%s\\(.+\\)$" - tab-width (1- tab-width) + whitespace-tab-width (1- whitespace-tab-width) (/ whitespace-line-column tab-width) - (let ((rem (% whitespace-line-column tab-width))) + (let ((rem (% whitespace-line-column whitespace-tab-width))) (if (zerop rem) "" (format ".\\{%d\\}" rem)))) - (if (memq 'lines whitespace-active-chars) + (if (memq 'lines whitespace-active-style) 0 ; whole line 2) ; line tail whitespace-line t)) t)) - (when (memq 'space-before-tab whitespace-active-chars) + (cond + ((memq 'space-before-tab whitespace-active-style) + (font-lock-add-keywords + nil + (list + ;; Show SPACEs before TAB (indent-tabs-mode) + (list whitespace-space-before-tab-regexp + (if whitespace-indent-tabs-mode 1 2) + whitespace-space-before-tab t)) + t)) + ((memq 'space-before-tab::tab whitespace-active-style) (font-lock-add-keywords nil (list - ;; Show SPACEs before TAB + ;; Show SPACEs before TAB (SPACEs) (list whitespace-space-before-tab-regexp 1 whitespace-space-before-tab t)) t)) - (when (memq 'indentation whitespace-active-chars) + ((memq 'space-before-tab::space whitespace-active-style) (font-lock-add-keywords nil (list - ;; Show indentation SPACEs - (list whitespace-indentation-regexp + ;; Show SPACEs before TAB (TABs) + (list whitespace-space-before-tab-regexp + 2 whitespace-space-before-tab t)) + t))) + (cond + ((memq 'indentation whitespace-active-style) + (font-lock-add-keywords + nil + (list + ;; Show indentation SPACEs (indent-tabs-mode) + (list (whitespace-indentation-regexp) 1 whitespace-indentation t)) t)) - (when (memq 'empty whitespace-active-chars) + ((memq 'indentation::tab whitespace-active-style) + (font-lock-add-keywords + nil + (list + ;; Show indentation SPACEs (SPACEs) + (list (whitespace-indentation-regexp 'tab) + 1 whitespace-indentation t)) + t)) + ((memq 'indentation::space whitespace-active-style) + (font-lock-add-keywords + nil + (list + ;; Show indentation SPACEs (TABs) + (list (whitespace-indentation-regexp 'space) + 1 whitespace-indentation t)) + t))) + (when (memq 'empty whitespace-active-style) (font-lock-add-keywords nil (list @@ -1647,22 +2253,39 @@ options are valid." (list whitespace-empty-at-eob-regexp 1 whitespace-empty t)) t)) - (when (memq 'space-after-tab whitespace-active-chars) + (cond + ((memq 'space-after-tab whitespace-active-style) + (font-lock-add-keywords + nil + (list + ;; Show SPACEs after TAB (indent-tabs-mode) + (list (whitespace-space-after-tab-regexp) + 1 whitespace-space-after-tab t)) + t)) + ((memq 'space-after-tab::tab whitespace-active-style) (font-lock-add-keywords nil (list - ;; Show SPACEs after TAB - (list whitespace-space-after-tab-regexp + ;; Show SPACEs after TAB (SPACEs) + (list (whitespace-space-after-tab-regexp 'tab) 1 whitespace-space-after-tab t)) t)) + ((memq 'space-after-tab::space whitespace-active-style) + (font-lock-add-keywords + nil + (list + ;; Show SPACEs after TAB (TABs) + (list (whitespace-space-after-tab-regexp 'space) + 1 whitespace-space-after-tab t)) + t))) ;; now turn on font lock and highlight blanks (font-lock-mode 1))) (defun whitespace-color-off () "Turn off color visualization." - (when whitespace-active-chars - ;; turn off font lock + ;; turn off font lock + (when (whitespace-style-face-p) (font-lock-mode 0) (when whitespace-font-lock (setq whitespace-font-lock nil @@ -1675,19 +2298,17 @@ options are valid." ;;;; Hacked from visws.el (Miles Bader ) -(defvar whitespace-display-table nil - "Used to save a local display table.") -(make-variable-buffer-local 'whitespace-display-table) - -(defvar whitespace-display-table-was-local nil - "Used to remember whether a buffer initially had a local display table.") -(make-variable-buffer-local 'whitespace-display-table-was-local) +(defun whitespace-style-mark-p () + "Return t if there is some visualization via display table." + (or (memq 'tab-mark whitespace-active-style) + (memq 'space-mark whitespace-active-style) + (memq 'newline-mark whitespace-active-style))) (defsubst whitespace-char-valid-p (char) ;; This check should be improved!!! (or (< char 256) - (char-valid-p char))) + (characterp char))) (defun whitespace-display-vector-p (vec) @@ -1701,7 +2322,8 @@ options are valid." (defun whitespace-display-char-on () "Turn on character display mapping." - (when whitespace-display-mappings + (when (and whitespace-display-mappings + (whitespace-style-mark-p)) (let (vecs vec) ;; Remember whether a buffer has a local display table. (unless whitespace-display-table-was-local @@ -1711,50 +2333,82 @@ options are valid." (unless buffer-display-table (setq buffer-display-table (make-display-table))) (dolist (entry whitespace-display-mappings) - (setq vecs (cdr entry)) - ;; Get a displayable mapping. - (while (and vecs - (not (whitespace-display-vector-p (car vecs)))) - (setq vecs (cdr vecs))) - ;; Display a valid mapping. - (when vecs - (setq vec (copy-sequence (car vecs))) - (cond - ;; Any char except newline - ((not (eq (car entry) ?\n)) - (aset buffer-display-table (car entry) vec)) - ;; Newline char - display it - ((memq 'newline whitespace-active-chars) - ;; Only insert face bits on NEWLINE char mapping to avoid - ;; obstruction of other faces like TABs and (HARD) SPACEs - ;; faces, font-lock faces, etc. - (when (memq 'color whitespace-active-style) + ;; check if it is to display this mark + (when (memq (car entry) whitespace-style) + ;; Get a displayable mapping. + (setq vecs (cddr entry)) + (while (and vecs + (not (whitespace-display-vector-p (car vecs)))) + (setq vecs (cdr vecs))) + ;; Display a valid mapping. + (when vecs + (setq vec (copy-sequence (car vecs))) + ;; NEWLINE char + (when (and (eq (cadr entry) ?\n) + (memq 'newline whitespace-active-style)) + ;; Only insert face bits on NEWLINE char mapping to avoid + ;; obstruction of other faces like TABs and (HARD) SPACEs + ;; faces, font-lock faces, etc. (dotimes (i (length vec)) - ;; Due to limitations of glyph representation, the char - ;; code can not be above ?\x1FFFF. Probably, this will - ;; be fixed after Emacs unicode merging. (or (eq (aref vec i) ?\n) - (> (aref vec i) #x1FFFF) (aset vec i (make-glyph-code (aref vec i) whitespace-newline))))) ;; Display mapping - (aset buffer-display-table (car entry) vec)) - ;; Newline char - don't display it - (t - ;; Do nothing - ))))))) + (aset buffer-display-table (cadr entry) vec))))))) (defun whitespace-display-char-off () "Turn off character display mapping." (and whitespace-display-mappings + (whitespace-style-mark-p) whitespace-display-table-was-local (setq whitespace-display-table-was-local nil buffer-display-table whitespace-display-table))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Hook + + +(defun whitespace-action-when-on () + "Action to be taken always when local whitespace is turned on." + (cond ((memq 'cleanup whitespace-action) + (whitespace-cleanup)) + ((memq 'report-on-bogus whitespace-action) + (whitespace-report nil t)))) + + +(defun whitespace-write-file-hook () + "Action to be taken when buffer is written. +It should be added buffer-locally to `write-file-functions'." + (cond ((memq 'auto-cleanup whitespace-action) + (whitespace-cleanup)) + ((memq 'abort-on-bogus whitespace-action) + (when (whitespace-report nil t) + (error "Abort write due to whitespace problems in %s" + (buffer-name))))) + nil) ; continue hook processing + + +(defun whitespace-warn-read-only (msg) + "Warn if buffer is read-only." + (when (memq 'warn-if-read-only whitespace-action) + (message "Can't %s: %s is read-only" msg (buffer-name)))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(defun whitespace-unload-function () + "Unload the whitespace library." + (global-whitespace-mode -1) + ;; be sure all local whitespace mode is turned off + (save-current-buffer + (dolist (buf (buffer-list)) + (set-buffer buf) + (whitespace-mode -1))) + nil) ; continue standard unloading (provide 'whitespace)