]> code.delx.au - gnu-emacs/blob - lisp/progmodes/glasses.el
(makefile-mode-abbrev-table): New variable.
[gnu-emacs] / lisp / progmodes / glasses.el
1 ;;; glasses.el --- make cantReadThis readable
2
3 ;; Copyright (C) 1999, 2000 Free Software Foundation, Inc.
4
5 ;; Author: Milan Zamazal <pdm@freesoft.cz>
6 ;; Maintainer: Milan Zamazal <pdm@freesoft.cz>
7 ;; Keywords: tools
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Commentary:
27
28 ;; This file defines a minor mode for making unreadableIdentifiersLikeThis
29 ;; readable. In some environments, for instance Java, it is common to use such
30 ;; unreadable identifiers. It is not good to use underscores in identifiers of
31 ;; your own project in such an environment to make your sources more readable,
32 ;; since it introduces undesirable confusion, which is worse than the
33 ;; unreadability. Fortunately, you use Emacs for the subproject, so the
34 ;; problem can be solved some way.
35 ;;
36 ;; This file defines the `glasses-mode' minor mode, which displays underscores
37 ;; between all the pairs of lower and upper English letters. (This only
38 ;; displays underscores, the text is not changed actually.) Alternatively, you
39 ;; can say you want the capitals in some given face (e.g. bold).
40 ;;
41 ;; The mode does something usable, though not perfect. Improvement suggestions
42 ;; from Emacs experts are welcome.
43 ;;
44 ;; If you like in-identifier separators different from underscores, change the
45 ;; value of the variable `glasses-separator' appropriately. See also the
46 ;; variables `glasses-face' and `glasses-convert-on-write-p'. You can also use
47 ;; the command `M-x customize-group RET glasses RET'.
48 ;;
49 ;; If you set any of the variables `glasses-separator' or `glasses-face' after
50 ;; glasses.el is loaded in a different way than through customize, you
51 ;; should call the function `glasses-set-overlay-properties' afterwards.
52
53 ;;; Code:
54
55
56 (eval-when-compile
57 (require 'cl))
58
59
60 ;;; User variables
61
62
63 (defgroup glasses nil
64 "Make unreadable code likeThis(one) readable."
65 :group 'tools)
66
67
68 (defcustom glasses-separator "_"
69 "*String to be displayed as a visual separator in unreadable identifiers."
70 :group 'glasses
71 :type 'string
72 :set 'glasses-custom-set
73 :initialize 'custom-initialize-default)
74
75
76 (defcustom glasses-face nil
77 "*Face to be put on capitals of an identifier looked through glasses.
78 If it is nil, no face is placed at the capitalized letter.
79
80 For example, you can set `glasses-separator' to an empty string and
81 `glasses-face' to `bold'. Then unreadable identifiers will have no separators,
82 but will have their capitals in bold."
83 :group 'glasses
84 :type 'symbol
85 :set 'glasses-custom-set
86 :initialize 'custom-initialize-default)
87
88
89 (defcustom glasses-separate-parentheses-p t
90 "*If non-nil, ensure space between an identifier and an opening parenthesis."
91 :group 'glasses
92 :type 'boolean)
93
94
95 (defcustom glasses-uncapitalize-p nil
96 "*If non-nil, downcase embedded capital letters in identifiers.
97 Only identifiers starting with lower case letters are affected, letters inside
98 other identifiers are unchanged."
99 :group 'glasses
100 :type 'boolean
101 :set 'glasses-custom-set
102 :initialize 'custom-initialize-default)
103
104
105 (defcustom glasses-uncapitalize-regexp "[a-z]"
106 "*Regexp matching beginnings of words to be uncapitalized.
107 Only words starting with this regexp are uncapitalized.
108 The regexp is case sensitive.
109 It has any effect only when `glasses-uncapitalize-p' is non-nil."
110 :group 'glasses
111 :type 'regexp
112 :set 'glasses-custom-set
113 :initialize 'custom-initialize-default)
114
115
116 (defcustom glasses-convert-on-write-p nil
117 "*If non-nil, remove separators when writing glasses buffer to a file.
118 If you are confused by glasses so much, that you write the separators into code
119 during coding, set this variable to t. The separators will be removed on each
120 file write then.
121
122 Note the removal action does not try to be much clever, so it can remove real
123 separators too."
124 :group 'glasses
125 :type 'boolean)
126
127
128 (defun glasses-custom-set (symbol value)
129 "Set value of the variable SYMBOL to VALUE and update overlay categories.
130 Used in :set parameter of some customized glasses variables."
131 (set symbol value)
132 (glasses-set-overlay-properties))
133
134
135 ;;; Utility functions
136
137
138 (defun glasses-set-overlay-properties ()
139 "Set properties of glasses overlays.
140 Consider current setting of user variables."
141 ;; In-identifier overlay
142 (put 'glasses 'evaporate t)
143 (put 'glasses 'before-string glasses-separator)
144 (put 'glasses 'face glasses-face)
145 ;; Beg-identifier overlay
146 (put 'glasses-init 'evaporate t)
147 (put 'glasses-init 'face glasses-face)
148 ;; Parenthesis overlay
149 (put 'glasses-parenthesis 'evaporate t)
150 (put 'glasses-parenthesis 'before-string " "))
151
152 (glasses-set-overlay-properties)
153
154
155 (defun glasses-overlay-p (overlay)
156 "Return whether OVERLAY is an overlay of glasses mode."
157 (memq (overlay-get overlay 'category)
158 '(glasses glasses-init glasses-parenthesis)))
159
160
161 (defun glasses-make-overlay (beg end &optional category)
162 "Create and return readability overlay over the region from BEG to END.
163 CATEGORY is the overlay category. If it is nil, use the `glasses' category."
164 (let ((overlay (make-overlay beg end)))
165 (overlay-put overlay 'category (or category 'glasses))
166 overlay))
167
168
169 (defun glasses-make-readable (beg end)
170 "Make identifiers in the region from BEG to END readable."
171 (let ((case-fold-search nil))
172 (save-excursion
173 (save-match-data
174 ;; Face only
175 (goto-char beg)
176 (while (re-search-forward
177 "\\<\\([A-Z]\\)[a-zA-Z]*\\([a-z][A-Z]\\|[A-Z][a-z]\\)"
178 end t)
179 (glasses-make-overlay (match-beginning 1) (match-end 1)
180 'glasses-init))
181 ;; Face + separator
182 (goto-char beg)
183 (while (re-search-forward "[a-z]\\([A-Z]\\)\\|[A-Z]\\([A-Z]\\)[a-z]"
184 end t)
185 (let* ((n (if (match-string 1) 1 2))
186 (o (glasses-make-overlay (match-beginning n) (match-end n))))
187 (goto-char (match-beginning n))
188 (when (and glasses-uncapitalize-p
189 (save-excursion
190 (save-match-data
191 (re-search-backward "\\<.")
192 (looking-at glasses-uncapitalize-regexp))))
193 (overlay-put o 'invisible t)
194 (overlay-put o 'after-string (downcase (match-string n))))))
195 ;; Parentheses
196 (when glasses-separate-parentheses-p
197 (goto-char beg)
198 (while (re-search-forward "[a-zA-Z]\\(\(\\)" end t)
199 (glasses-make-overlay (match-beginning 1) (match-end 1)
200 'glasses-parenthesis)))))))
201
202
203 (defun glasses-make-unreadable (beg end)
204 "Return identifiers in the region from BEG to END to their unreadable state."
205 (dolist (o (overlays-in beg end))
206 (when (glasses-overlay-p o)
207 (delete-overlay o))))
208
209
210 (defun glasses-convert-to-unreadable ()
211 "Convert current buffer to unreadable identifiers and return nil.
212 This function modifies buffer contents, it removes all the separators,
213 recognized according to the current value of the variable `glasses-separator'."
214 (when (and glasses-convert-on-write-p
215 (not (string= glasses-separator "")))
216 (let ((case-fold-search nil))
217 (save-excursion
218 (goto-char (point-min))
219 (while (re-search-forward
220 "[a-z]\\(_\\)[A-Z]\\|[A-Z]\\(_\\)[A-Z][a-z]" nil t)
221 (let ((n (if (match-string 1) 1 2)))
222 (replace-match "" t nil nil n)
223 (goto-char (match-end n))))
224 (when glasses-separate-parentheses-p
225 (goto-char (point-min))
226 (while (re-search-forward "[a-zA-Z]\\( \\)\(" nil t)
227 (replace-match "" t nil nil 1))))))
228 ;; nil must be returned to allow use in write file hooks
229 nil)
230
231
232 (defun glasses-change (beg end old-len)
233 "After-change function updating glass overlays."
234 (let ((beg-line (save-excursion (goto-char beg) (line-beginning-position)))
235 (end-line (save-excursion (goto-char end) (line-end-position))))
236 (glasses-make-unreadable beg-line end-line)
237 (glasses-make-readable beg-line end-line)))
238
239
240 ;;; Minor mode definition
241
242
243 (defvar glasses-mode nil
244 "Mode variable for `glasses-mode'.")
245 (make-variable-buffer-local 'glasses-mode)
246
247 (add-to-list 'minor-mode-alist '(glasses-mode " o^o"))
248
249
250 ;;;###autoload
251 (defun glasses-mode (arg)
252 "Minor mode for making identifiers likeThis readable.
253 When this mode is active, it tries to add virtual separators (like underscores)
254 at places they belong to."
255 (interactive "P")
256 (let ((new-flag (if (null arg)
257 (not glasses-mode)
258 (> (prefix-numeric-value arg) 0))))
259 (unless (eq new-flag glasses-mode)
260 (save-excursion
261 (save-restriction
262 (widen)
263 (if new-flag
264 (progn
265 ;; We erase the all overlays to avoid dual sight in some
266 ;; circumstances
267 (glasses-make-unreadable (point-min) (point-max))
268 (glasses-make-readable (point-min) (point-max))
269 (make-local-hook 'after-change-functions)
270 (add-hook 'after-change-functions 'glasses-change nil t)
271 (add-hook 'local-write-file-hooks
272 'glasses-convert-to-unreadable nil t))
273 (glasses-make-unreadable (point-min) (point-max))
274 (remove-hook 'after-change-functions 'glasses-change t)
275 (remove-hook 'local-write-file-hooks
276 'glasses-convert-to-unreadable t))))
277 (setq glasses-mode new-flag))))
278
279
280 ;;; Announce
281
282 (provide 'glasses)
283
284
285 ;;; glasses.el ends here