(c++-imenu-generic-expression): Var defined.
[bpt/emacs.git] / lisp / progmodes / cpp.el
CommitLineData
20f5d145
RS
1;;; cpp.el --- Highlight or hide text according to cpp conditionals.
2
2e922f0b 3;; Copyright (C) 1994, 1995 Free Software Foundation
20f5d145
RS
4
5;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
20f5d145
RS
6;; Keywords: c, faces, tools
7
7930d722 8;; This file is part of GNU Emacs.
20f5d145 9
7930d722 10;; GNU Emacs is free software; you can redistribute it and/or modify
20f5d145
RS
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
7930d722
RS
14
15;; GNU Emacs is distributed in the hope that it will be useful,
20f5d145
RS
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
7930d722 19
20f5d145 20;; You should have received a copy of the GNU General Public License
7930d722
RS
21;; along with GNU Emacs; see the file COPYING. If not, write to
22;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20f5d145 23
7930d722 24;;; Commentary:
20f5d145
RS
25
26;; Parse a text for C preprocessor conditionals, and highlight or hide
27;; the text inside the conditionals as you wish.
28
fe441eb7 29;; This package is inspired by Jim Coplien's delta editor for SCCS.
20f5d145
RS
30
31;;; Todo:
32
33;; Should parse "#if" and "#elif" expressions and merge the faces
34;; somehow.
35
36;; Somehow it is sometimes possible to make changes near a read only
37;; area which you can't undo. Their are other strange effects in that
38;; area.
39
40;; The Edit buffer should -- optionally -- appear in its own frame.
41
42;; Conditionals seem to be rear-sticky. They shouldn't be.
43
44;; Restore window configurations when exiting CPP Edit buffer.
45
46;;; Code:
47
48;;; Customization:
49
50(defvar cpp-known-face 'invisible
51 "*Face used for known cpp symbols.")
52
53(defvar cpp-unknown-face 'highlight
54 "*Face used for unknown cpp cymbols.")
55
56(defvar cpp-face-type 'light
57 "*Indicate what background face type you prefer.
58Can be either light or dark for color screens, mono for monochrome
59screens, and none if you don't use a window system.")
60
61(defvar cpp-known-writable t
62 "*Non-nil means you are allowed to modify the known conditionals.")
63
64(defvar cpp-unknown-writable t
65 "*Non-nil means you are allowed to modify the unknown conditionals.")
66
2e922f0b
RS
67(defvar cpp-edit-list nil
68 "Alist of cpp macros and information about how they should be displayed.
69Each entry is a list with the following elements:
700. The name of the macro (a string).
711. Face used for text that is `ifdef' the macro.
722. Face used for text that is `ifndef' the macro.
733. `t', `nil', or `both' depending on what text may be edited.")
74
75(defvar cpp-overlay-list nil)
76;; List of cpp overlays active in the current buffer.
77(make-variable-buffer-local 'cpp-overlay-list)
78
79(defvar cpp-callback-data)
80(defvar cpp-state-stack)
81
82(defconst cpp-face-type-list
83 '(("light color background" . light)
84 ("dark color background" . dark)
85 ("monochrome" . mono)
86 ("tty" . none))
87 "Alist of strings and names of the defined face collections.")
88
89(defconst cpp-writable-list
90 ;; Names used for the writable property.
91 '(("writable" . t)
92 ("read-only" . nil)))
93
94(defvar cpp-button-event nil)
95;; This will be t in the callback for `cpp-make-button'.
96
97(defvar cpp-edit-buffer nil)
98;; Real buffer whose cpp display information we are editing.
99(make-variable-buffer-local 'cpp-edit-buffer)
100
101(defconst cpp-branch-list
102 ;; Alist of branches.
103 '(("false" . nil)
104 ("true" . t)
105 ("both" . both)))
106
107(defvar cpp-face-default-list nil
108 "List of faces you can choose from for cpp conditionals.")
109
110(defvar cpp-face-light-name-list
111 '("light gray" "light blue" "light cyan" "light yellow" "light pink"
112 "pale green" "beige" "orange" "magenta" "violet" "medium purple"
113 "turquoise")
114 "Background colours useful with dark foreground colors.")
115
116(defvar cpp-face-dark-name-list
117 '("dim gray" "blue" "cyan" "yellow" "red"
118 "dark green" "brown" "dark orange" "dark khaki" "dark violet" "purple"
119 "dark turquoise")
120 "Background colours useful with light foreground colors.")
121
122(defvar cpp-face-light-list nil
123 "Alist of names and faces to be used for light backgrounds.")
124
125(defvar cpp-face-dark-list nil
126 "Alist of names and faces to be used for dark backgrounds.")
127
128(defvar cpp-face-mono-list
129 '(("bold" . 'bold)
130 ("bold-italic" . 'bold-italic)
131 ("italic" . 'italic)
132 ("underline" . 'underline))
133 "Alist of names and faces to be used for monocrome screens.")
134
135(defvar cpp-face-none-list
136 '(("default" . default)
137 ("invisible" . invisible))
138 "Alist of names and faces available even if you don't use a window system.")
139
140(defvar cpp-face-all-list
141 (append cpp-face-light-list
142 cpp-face-dark-list
143 cpp-face-mono-list
144 cpp-face-none-list)
145 "All faces used for highligting text inside cpp conditionals.")
146
20f5d145
RS
147;;; Parse Buffer:
148
149(defvar cpp-parse-symbols nil
150 "List of cpp macros used in the local buffer.")
151(make-variable-buffer-local 'cpp-parse-symbols)
152
153(defconst cpp-parse-regexp
154 ;; Regexp matching all tokens needed to find conditionals.
155 (concat
156 "'\\|\"\\|/\\*\\|//\\|"
157 "\\(^[ \t]*#[ \t]*\\(ifdef\\|ifndef\\|if\\|"
158 "elif\\|else\\|endif\\)\\b\\)"))
159
160;;;###autoload
daf4206b
RS
161(defun cpp-highlight-buffer (arg)
162 "Highlight C code according to preprocessor conditionals.
163This command pops up a buffer which you should edit to specify
164what kind of highlighting to use, and the criteria for highlighting.
f3a69d8f 165A prefix arg suppresses display of that buffer."
20f5d145
RS
166 (interactive "P")
167 (setq cpp-parse-symbols nil)
168 (cpp-parse-reset)
169 (if (null cpp-edit-list)
170 (cpp-edit-load))
2e922f0b 171 (let (cpp-state-stack)
20f5d145
RS
172 (save-excursion
173 (goto-char (point-min))
174 (cpp-progress-message "Parsing...")
175 (while (re-search-forward cpp-parse-regexp nil t)
176 (cpp-progress-message "Parsing...%d%%"
177 (/ (* 100 (- (point) (point-min))) (buffer-size)))
178 (let ((match (buffer-substring (match-beginning 0) (match-end 0))))
179 (cond ((or (string-equal match "'")
180 (string-equal match "\""))
181 (goto-char (match-beginning 0))
182 (condition-case nil
183 (forward-sexp)
184 (error (cpp-parse-error
185 "Unterminated string or character"))))
186 ((string-equal match "/*")
187 (or (search-forward "*/" nil t)
188 (error "Unterminated comment")))
189 ((string-equal match "//")
190 (skip-chars-forward "^\n\r"))
191 (t
192 (end-of-line 1)
193 (let ((from (match-beginning 1))
194 (to (1+ (point)))
195 (type (buffer-substring (match-beginning 2)
196 (match-end 2)))
197 (expr (buffer-substring (match-end 1) (point))))
198 (cond ((string-equal type "ifdef")
199 (cpp-parse-open t expr from to))
200 ((string-equal type "ifndef")
201 (cpp-parse-open nil expr from to))
202 ((string-equal type "if")
203 (cpp-parse-open t expr from to))
204 ((string-equal type "elif")
205 (let (cpp-known-face cpp-unknown-face)
206 (cpp-parse-close from to))
207 (cpp-parse-open t expr from to))
208 ((string-equal type "else")
2e922f0b
RS
209 (or cpp-state-stack
210 (cpp-parse-error "Top level #else"))
211 (let ((entry (list (not (nth 0 (car cpp-state-stack)))
212 (nth 1 (car cpp-state-stack))
20f5d145
RS
213 from to)))
214 (cpp-parse-close from to)
2e922f0b 215 (setq cpp-state-stack (cons entry cpp-state-stack))))
20f5d145
RS
216 ((string-equal type "endif")
217 (cpp-parse-close from to))
218 (t
219 (cpp-parse-error "Parser error"))))))))
220 (message "Parsing...done"))
2e922f0b 221 (if cpp-state-stack
20f5d145 222 (save-excursion
2e922f0b 223 (goto-char (nth 3 (car cpp-state-stack)))
20f5d145
RS
224 (cpp-parse-error "Unclosed conditional"))))
225 (or arg
226 (null cpp-parse-symbols)
227 (cpp-parse-edit)))
228
229(defun cpp-parse-open (branch expr begin end)
2e922f0b 230 "Push information about conditional-beginning onto `cpp-state-stack'."
f3a69d8f 231 ;; Discard comments within this line.
20f5d145
RS
232 (while (string-match "\\b[ \t]*/\\*.*\\*/[ \t]*\\b" expr)
233 (setq expr (concat (substring expr 0 (match-beginning 0))
234 (substring expr (match-end 0)))))
f3a69d8f
RS
235 ;; If a comment starts on this line and continues past, discard it.
236 (if (string-match "\\b[ \t]*/\\*" expr)
237 (setq expr (substring expr 0 (match-beginning 0))))
238 ;; Delete any C++ comment from the line.
20f5d145
RS
239 (if (string-match "\\b[ \t]*\\(//.*\\)?$" expr)
240 (setq expr (substring expr 0 (match-beginning 0))))
241 (while (string-match "[ \t]+" expr)
242 (setq expr (concat (substring expr 0 (match-beginning 0))
243 (substring expr (match-end 0)))))
2e922f0b 244 (setq cpp-state-stack (cons (list branch expr begin end) cpp-state-stack))
20f5d145
RS
245 (or (member expr cpp-parse-symbols)
246 (setq cpp-parse-symbols
247 (cons expr cpp-parse-symbols)))
248 (if (assoc expr cpp-edit-list)
249 (cpp-make-known-overlay begin end)
250 (cpp-make-unknown-overlay begin end)))
251
252(defun cpp-parse-close (from to)
2e922f0b
RS
253 ;; Pop top of cpp-state-stack and create overlay.
254 (let ((entry (assoc (nth 1 (car cpp-state-stack)) cpp-edit-list))
255 (branch (nth 0 (car cpp-state-stack)))
256 (begin (nth 2 (car cpp-state-stack)))
257 (end (nth 3 (car cpp-state-stack))))
258 (setq cpp-state-stack (cdr cpp-state-stack))
20f5d145
RS
259 (if entry
260 (let ((face (nth (if branch 1 2) entry))
261 (read-only (eq (not branch) (nth 3 entry)))
2e922f0b 262 (priority (length cpp-state-stack))
20f5d145
RS
263 (overlay (make-overlay end from)))
264 (cpp-make-known-overlay from to)
265 (setq cpp-overlay-list (cons overlay cpp-overlay-list))
266 (if priority (overlay-put overlay 'priority priority))
267 (cond ((eq face 'invisible)
268 (cpp-make-overlay-hidden overlay))
269 ((eq face 'default))
270 (t
271 (overlay-put overlay 'face face)))
272 (if read-only
273 (cpp-make-overlay-read-only overlay)
274 (cpp-make-overlay-sticky overlay)))
275 (cpp-make-unknown-overlay from to))))
276
277(defun cpp-parse-error (error)
278 ;; Error message issued by the cpp parser.
279 (error (concat error " at line %d") (count-lines (point-min) (point))))
280
281(defun cpp-parse-reset ()
282 "Reset display of cpp conditionals to normal."
283 (interactive)
284 (while cpp-overlay-list
285 (delete-overlay (car cpp-overlay-list))
286 (setq cpp-overlay-list (cdr cpp-overlay-list))))
287
288;;;###autoload
289(defun cpp-parse-edit ()
290 "Edit display information for cpp conditionals."
291 (interactive)
292 (or cpp-parse-symbols
f3a69d8f 293 (cpp-highlight-buffer t))
20f5d145
RS
294 (let ((buffer (current-buffer)))
295 (pop-to-buffer "*CPP Edit*")
296 (cpp-edit-mode)
297 (setq cpp-edit-buffer buffer)
298 (cpp-edit-reset)))
299
300;;; Overlays:
301
20f5d145
RS
302(defun cpp-make-known-overlay (start end)
303 ;; Create an overlay for a known cpp command from START to END.
304 (let ((overlay (make-overlay start end)))
305 (if (eq cpp-known-face 'invisible)
306 (cpp-make-overlay-hidden overlay)
307 (or (eq cpp-known-face 'default)
308 (overlay-put overlay 'face cpp-known-face))
309 (if cpp-known-writable
310 ()
311 (overlay-put overlay 'modification-hooks '(cpp-signal-read-only))
312 (overlay-put overlay 'insert-in-front-hooks '(cpp-signal-read-only))))
313 (setq cpp-overlay-list (cons overlay cpp-overlay-list))))
314
315(defun cpp-make-unknown-overlay (start end)
316 ;; Create an overlay for an unknown cpp command from START to END.
317 (let ((overlay (make-overlay start end)))
318 (cond ((eq cpp-unknown-face 'invisible)
319 (cpp-make-overlay-hidden overlay))
320 ((eq cpp-unknown-face 'default))
321 (t
322 (overlay-put overlay 'face cpp-unknown-face)))
323 (if cpp-unknown-writable
324 ()
325 (overlay-put overlay 'modification-hooks '(cpp-signal-read-only))
326 (overlay-put overlay 'insert-in-front-hooks '(cpp-signal-read-only)))
327 (setq cpp-overlay-list (cons overlay cpp-overlay-list))))
328
329(defun cpp-make-overlay-hidden (overlay)
330 ;; Make overlay hidden and intangible.
331 (overlay-put overlay 'invisible t)
332 (overlay-put overlay 'intangible t)
333 ;; Unfortunately `intangible' is not implemented for overlays yet,
334 ;; so we make is read-only instead.
dfc4f59b
RS
335 (overlay-put overlay 'modification-hooks '(cpp-signal-read-only))
336 (overlay-put overlay 'insert-in-front-hooks '(cpp-signal-read-only)))
20f5d145
RS
337
338(defun cpp-make-overlay-read-only (overlay)
339 ;; Make overlay read only.
340 (overlay-put overlay 'modification-hooks '(cpp-signal-read-only))
341 (overlay-put overlay 'insert-in-front-hooks '(cpp-signal-read-only))
342 (overlay-put overlay 'insert-behind-hooks '(cpp-signal-read-only)))
343
344(defun cpp-make-overlay-sticky (overlay)
345 ;; Make OVERLAY grow when you insert text at either end.
346 (overlay-put overlay 'insert-in-front-hooks '(cpp-grow-overlay))
347 (overlay-put overlay 'insert-behind-hooks '(cpp-grow-overlay)))
348
dfc4f59b 349(defun cpp-signal-read-only (overlay after start end &optional len)
20f5d145
RS
350 ;; Only allow deleting the whole overlay.
351 ;; Trying to change a read-only overlay.
dfc4f59b
RS
352 (if (and (not after)
353 (or (< (overlay-start overlay) start)
354 (> (overlay-end overlay) end)))
20f5d145
RS
355 (error "This text is read only")))
356
dfc4f59b 357(defun cpp-grow-overlay (overlay after start end &optional len)
20f5d145 358 ;; Make OVERLAY grow to contain range START to END.
dfc4f59b
RS
359 (if after
360 (move-overlay overlay
361 (min start (overlay-start overlay))
362 (max end (overlay-end overlay)))))
20f5d145
RS
363
364;;; Edit Buffer:
365
20f5d145
RS
366(defvar cpp-edit-map nil)
367;; Keymap for `cpp-edit-mode'.
368
369(if cpp-edit-map
370 ()
371 (setq cpp-edit-map (make-keymap))
372 (suppress-keymap cpp-edit-map)
373 (define-key cpp-edit-map [ down-mouse-2 ] 'cpp-push-button)
374 (define-key cpp-edit-map [ mouse-2 ] 'ignore)
375 (define-key cpp-edit-map " " 'scroll-up)
376 (define-key cpp-edit-map "\C-?" 'scroll-down)
377 (define-key cpp-edit-map [ delete ] 'scroll-down)
378 (define-key cpp-edit-map "\C-c\C-c" 'cpp-edit-apply)
379 (define-key cpp-edit-map "a" 'cpp-edit-apply)
380 (define-key cpp-edit-map "A" 'cpp-edit-apply)
381 (define-key cpp-edit-map "r" 'cpp-edit-reset)
382 (define-key cpp-edit-map "R" 'cpp-edit-reset)
383 (define-key cpp-edit-map "s" 'cpp-edit-save)
384 (define-key cpp-edit-map "S" 'cpp-edit-save)
385 (define-key cpp-edit-map "l" 'cpp-edit-load)
386 (define-key cpp-edit-map "L" 'cpp-edit-load)
387 (define-key cpp-edit-map "h" 'cpp-edit-home)
388 (define-key cpp-edit-map "H" 'cpp-edit-home)
389 (define-key cpp-edit-map "b" 'cpp-edit-background)
390 (define-key cpp-edit-map "B" 'cpp-edit-background)
391 (define-key cpp-edit-map "k" 'cpp-edit-known)
392 (define-key cpp-edit-map "K" 'cpp-edit-known)
393 (define-key cpp-edit-map "u" 'cpp-edit-unknown)
394 (define-key cpp-edit-map "u" 'cpp-edit-unknown)
395 (define-key cpp-edit-map "t" 'cpp-edit-true)
396 (define-key cpp-edit-map "T" 'cpp-edit-true)
397 (define-key cpp-edit-map "f" 'cpp-edit-false)
398 (define-key cpp-edit-map "F" 'cpp-edit-false)
399 (define-key cpp-edit-map "w" 'cpp-edit-write)
400 (define-key cpp-edit-map "W" 'cpp-edit-write)
401 (define-key cpp-edit-map "X" 'cpp-edit-toggle-known)
402 (define-key cpp-edit-map "x" 'cpp-edit-toggle-known)
403 (define-key cpp-edit-map "Y" 'cpp-edit-toggle-unknown)
404 (define-key cpp-edit-map "y" 'cpp-edit-toggle-unknown)
405 (define-key cpp-edit-map "q" 'bury-buffer)
406 (define-key cpp-edit-map "Q" 'bury-buffer))
407
20f5d145
RS
408(defvar cpp-edit-symbols nil)
409;; Symbols defined in the edit buffer.
410(make-variable-buffer-local 'cpp-edit-symbols)
411
412(defun cpp-edit-mode ()
daf4206b 413 "Major mode for editing the criteria for highlighting cpp conditionals.
20f5d145
RS
414Click on objects to change them.
415You can also use the keyboard accelerators indicated like this: [K]ey."
416 (kill-all-local-variables)
417 (buffer-disable-undo)
418 (auto-save-mode -1)
419 (setq buffer-read-only t)
420 (setq major-mode 'cpp-edit-mode)
421 (setq mode-name "CPP Edit")
422 (use-local-map cpp-edit-map))
423
424(defun cpp-edit-apply ()
425 "Apply edited display information to original buffer."
426 (interactive)
427 (cpp-edit-home)
f3a69d8f 428 (cpp-highlight-buffer t))
20f5d145
RS
429
430(defun cpp-edit-reset ()
431 "Reset display information from original buffer."
432 (interactive)
433 (let ((buffer (current-buffer))
434 (buffer-read-only nil)
435 (start (window-start))
436 (pos (point))
437 symbols)
438 (set-buffer cpp-edit-buffer)
439 (setq symbols cpp-parse-symbols)
440 (set-buffer buffer)
441 (setq cpp-edit-symbols symbols)
442 (erase-buffer)
443 (insert "CPP Display Information for `")
444 (cpp-make-button (buffer-name cpp-edit-buffer) 'cpp-edit-home)
20f5d145 445 (insert "\n\nClick mouse-2 on item you want to change or use\n"
f3a69d8f
RS
446 "or switch to this buffer and type the keyboard equivalents.\n"
447 "Keyboard equivalents are indicated with brackets like [T]his.\n\n")
448 (cpp-make-button "[H]ome (display the C file)" 'cpp-edit-home)
449 (insert " ")
450 (cpp-make-button "[A]pply new settings" 'cpp-edit-apply)
451 (insert "\n")
452 (cpp-make-button "[S]ave settings" 'cpp-edit-save)
453 (insert " ")
454 (cpp-make-button "[L]oad settings" 'cpp-edit-load)
455 (insert "\n\n")
456
20f5d145
RS
457 (insert "[B]ackground: ")
458 (cpp-make-button (car (rassq cpp-face-type cpp-face-type-list))
459 'cpp-edit-background)
460 (insert "\n[K]nown conditionals: ")
461 (cpp-make-button (cpp-face-name cpp-known-face)
462 'cpp-edit-known nil t)
463 (insert " [X] ")
464 (cpp-make-button (car (rassq cpp-known-writable cpp-writable-list))
465 'cpp-edit-toggle-known)
466 (insert "\n[U]nknown conditionals: ")
467 (cpp-make-button (cpp-face-name cpp-unknown-face)
468 'cpp-edit-unknown nil t)
469 (insert " [Y] ")
470 (cpp-make-button (car (rassq cpp-unknown-writable cpp-writable-list))
471 'cpp-edit-toggle-unknown)
472 (insert (format "\n\n\n%39s: %14s %14s %7s\n\n" "Expression"
473 "[T]rue Face" "[F]alse Face" "[W]rite"))
474 (while symbols
475 (let* ((symbol (car symbols))
476 (entry (assoc symbol cpp-edit-list))
477 (true (nth 1 entry))
478 (false (nth 2 entry))
479 (write (if entry (nth 3 entry) 'both)))
480 (setq symbols (cdr symbols))
481
482 (if (and entry ; Make default entries unknown.
483 (or (null true) (eq true 'default))
484 (or (null false) (eq false 'default))
485 (eq write 'both))
486 (setq cpp-edit-list (delq entry cpp-edit-list)
487 entry nil))
488
dfc4f59b 489 (if (> (length symbol) 39)
20f5d145
RS
490 (insert (substring symbol 0 39) ": ")
491 (insert (format "%39s: " symbol)))
492
493 (cpp-make-button (cpp-face-name true)
494 'cpp-edit-true symbol t 14)
495 (insert " ")
496 (cpp-make-button (cpp-face-name false)
497 'cpp-edit-false symbol t 14)
498 (insert " ")
499 (cpp-make-button (car (rassq write cpp-branch-list))
500 'cpp-edit-write symbol nil 6)
501 (insert "\n")))
502 (insert "\n\n")
503 (set-window-start nil start)
504 (goto-char pos)))
505
506(defun cpp-edit-load ()
507 "Load cpp configuration."
508 (interactive)
509 (cond ((file-readable-p ".cpp.el")
510 (load-file ".cpp.el"))
511 ((file-readable-p "~/.cpp.el")
512 (load-file ".cpp.el")))
fe441eb7
RS
513 (if (eq major-mode 'cpp-edit-mode)
514 (cpp-edit-reset)))
20f5d145
RS
515
516(defun cpp-edit-save ()
517 "Load cpp configuration."
518 (interactive)
519 (require 'pp)
520 (save-excursion
521 (set-buffer cpp-edit-buffer)
522 (let ((buffer (find-file-noselect ".cpp.el")))
523 (set-buffer buffer)
524 (erase-buffer)
525 (pp (list 'setq 'cpp-known-face
526 (list 'quote cpp-known-face)) buffer)
527 (pp (list 'setq 'cpp-unknown-face
528 (list 'quote cpp-unknown-face)) buffer)
529 (pp (list 'setq 'cpp-face-type
530 (list 'quote cpp-face-type)) buffer)
531 (pp (list 'setq 'cpp-known-writable
532 (list 'quote cpp-known-writable)) buffer)
533 (pp (list 'setq 'cpp-unknown-writable
534 (list 'quote cpp-unknown-writable)) buffer)
535 (pp (list 'setq 'cpp-edit-list
536 (list 'quote cpp-edit-list)) buffer)
537 (write-file ".cpp.el"))))
538
539(defun cpp-edit-home ()
540 "Switch back to original buffer."
541 (interactive)
542 (if cpp-button-event
543 (read-event))
544 (pop-to-buffer cpp-edit-buffer))
545
546(defun cpp-edit-background ()
547 "Change default face collection."
548 (interactive)
549 (call-interactively 'cpp-choose-default-face)
550 (cpp-edit-reset))
551
552(defun cpp-edit-known ()
553 "Select default for known conditionals."
554 (interactive)
555 (setq cpp-known-face (cpp-choose-face "Known face" cpp-known-face))
556 (cpp-edit-reset))
557
558(defun cpp-edit-unknown ()
559 "Select default for unknown conditionals."
560 (interactive)
561 (setq cpp-unknown-face (cpp-choose-face "Unknown face" cpp-unknown-face))
562 (cpp-edit-reset))
563
20f5d145
RS
564(defun cpp-edit-toggle-known (arg)
565 "Toggle writable status for known conditionals.
566With optional argument ARG, make them writable iff ARG is positive."
567 (interactive "@P")
568 (if (or (and (null arg) cpp-known-writable)
569 (<= (prefix-numeric-value arg) 0))
570 (setq cpp-known-writable nil)
571 (setq cpp-known-writable t))
572 (cpp-edit-reset))
573
574(defun cpp-edit-toggle-unknown (arg)
575 "Toggle writable status for unknown conditionals.
576With optional argument ARG, make them writable iff ARG is positive."
577 (interactive "@P")
578 (if (or (and (null arg) cpp-unknown-writable)
579 (<= (prefix-numeric-value arg) 0))
580 (setq cpp-unknown-writable nil)
581 (setq cpp-unknown-writable t))
582 (cpp-edit-reset))
583
584(defun cpp-edit-true (symbol face)
585 "Select SYMBOL's true FACE used for highlighting taken conditionals."
586 (interactive
587 (let ((symbol (cpp-choose-symbol)))
588 (list symbol
589 (cpp-choose-face "True face"
590 (nth 1 (assoc symbol cpp-edit-list))))))
591 (setcar (nthcdr 1 (cpp-edit-list-entry-get-or-create symbol)) face)
592 (cpp-edit-reset))
593
594(defun cpp-edit-false (symbol face)
595 "Select SYMBOL's false FACE used for highlighting untaken conditionals."
596 (interactive
597 (let ((symbol (cpp-choose-symbol)))
598 (list symbol
599 (cpp-choose-face "False face"
600 (nth 2 (assoc symbol cpp-edit-list))))))
601 (setcar (nthcdr 2 (cpp-edit-list-entry-get-or-create symbol)) face)
602 (cpp-edit-reset))
603
604(defun cpp-edit-write (symbol branch)
605 "Set which branches of SYMBOL should be writable to BRANCH.
606BRANCH should be either nil (false branch), t (true branch) or 'both."
607 (interactive (list (cpp-choose-symbol) (cpp-choose-branch)))
608 (setcar (nthcdr 3 (cpp-edit-list-entry-get-or-create symbol)) branch)
609 (cpp-edit-reset))
610
611(defun cpp-edit-list-entry-get-or-create (symbol)
612 ;; Return the entry for SYMBOL in `cpp-edit-list'.
613 ;; If it does not exist, create it.
614 (let ((entry (assoc symbol cpp-edit-list)))
615 (or entry
616 (setq entry (list symbol nil nil 'both nil)
617 cpp-edit-list (cons entry cpp-edit-list)))
618 entry))
619
620;;; Prompts:
621
622(defun cpp-choose-symbol ()
623 ;; Choose a symbol if called from keyboard, otherwise use the one clicked on.
624 (if cpp-button-event
2e922f0b 625 cpp-callback-data
20f5d145
RS
626 (completing-read "Symbol: " (mapcar 'list cpp-edit-symbols) nil t)))
627
20f5d145
RS
628(defun cpp-choose-branch ()
629 ;; Choose a branch, either nil, t, or both.
630 (if cpp-button-event
631 (x-popup-menu cpp-button-event
632 (list "Branch" (cons "Branch" cpp-branch-list)))
633 (cdr (assoc (completing-read "Branch: " cpp-branch-list nil t)
634 cpp-branch-list))))
635
636(defun cpp-choose-face (prompt default)
637 ;; Choose a face from cpp-face-defalt-list.
638 ;; PROMPT is what to say to the user.
639 ;; DEFAULT is the default face.
640 (or (if cpp-button-event
641 (x-popup-menu cpp-button-event
642 (list prompt (cons prompt cpp-face-default-list)))
643 (let ((name (car (rassq default cpp-face-default-list))))
644 (cdr (assoc (completing-read (if name
645 (concat prompt
646 " (default " name "): ")
647 (concat prompt ": "))
648 cpp-face-default-list nil t)
649 cpp-face-all-list))))
650 default))
651
20f5d145
RS
652(defun cpp-choose-default-face (type)
653 ;; Choose default face list for screen of TYPE.
654 ;; Type must be one of the types defined in `cpp-face-type-list'.
655 (interactive (list (if cpp-button-event
656 (x-popup-menu cpp-button-event
657 (list "Screen type"
658 (cons "Screen type"
659 cpp-face-type-list)))
660 (cdr (assoc (completing-read "Screen type: "
661 cpp-face-type-list
662 nil t)
663 cpp-face-type-list)))))
664 (cond ((null type))
665 ((eq type 'light)
666 (if cpp-face-light-list
667 ()
668 (setq cpp-face-light-list
669 (mapcar 'cpp-create-bg-face cpp-face-light-name-list))
670 (setq cpp-face-all-list
671 (append cpp-face-all-list cpp-face-light-list)))
672 (setq cpp-face-type 'light)
673 (setq cpp-face-default-list
674 (append cpp-face-light-list cpp-face-none-list)))
675 ((eq type 'dark)
676 (if cpp-face-dark-list
677 ()
678 (setq cpp-face-dark-list
679 (mapcar 'cpp-create-bg-face cpp-face-dark-name-list))
680 (setq cpp-face-all-list
681 (append cpp-face-all-list cpp-face-dark-list)))
682 (setq cpp-face-type 'dark)
683 (setq cpp-face-default-list
684 (append cpp-face-dark-list cpp-face-none-list)))
685 ((eq type 'mono)
686 (setq cpp-face-type 'mono)
687 (setq cpp-face-default-list
688 (append cpp-face-mono-list cpp-face-none-list)))
689 (t
690 (setq cpp-face-type 'none)
691 (setq cpp-face-default-list cpp-face-none-list))))
692
693;;; Buttons:
694
20f5d145
RS
695(defun cpp-make-button (name callback &optional data face padding)
696 ;; Create a button at point.
697 ;; NAME is the name of the button.
698 ;; CALLBACK is the function to call when the button is pushed.
2e922f0b
RS
699 ;; DATA will be made available to CALLBACK
700 ;;in the free variable cpp-callback-data.
20f5d145
RS
701 ;; FACE means that NAME is the name of a face in `cpp-face-all-list'.
702 ;; PADDING means NAME will be right justified at that length.
703 (let ((name (format "%s" name))
704 from to)
705 (cond ((null padding)
706 (setq from (point))
707 (insert name))
708 ((> (length name) padding)
709 (setq from (point))
710 (insert (substring name 0 padding)))
711 (t
712 (insert (make-string (- padding (length name)) ? ))
713 (setq from (point))
714 (insert name)))
715 (setq to (point))
716 (setq face
717 (if face
718 (let ((check (cdr (assoc name cpp-face-all-list))))
719 (if (memq check '(default invisible))
720 'bold
721 check))
722 'bold))
723 (add-text-properties from to
724 (append (list 'face face)
725 '(mouse-face highlight)
726 (list 'cpp-callback callback)
727 (if data (list 'cpp-data data))))))
728
729(defun cpp-push-button (event)
730 ;; Pushed a CPP button.
731 (interactive "@e")
732 (set-buffer (window-buffer (posn-window (event-start event))))
733 (let ((pos (posn-point (event-start event))))
2e922f0b 734 (let ((cpp-callback-data (get-text-property pos 'cpp-data))
20f5d145
RS
735 (fun (get-text-property pos 'cpp-callback))
736 (cpp-button-event event))
737 (cond (fun
738 (call-interactively (get-text-property pos 'cpp-callback)))
739 ((lookup-key global-map [ down-mouse-2])
740 (call-interactively (lookup-key global-map [ down-mouse-2])))))))
741
742;;; Faces:
743
20f5d145
RS
744(defun cpp-create-bg-face (color)
745 ;; Create entry for face with background COLOR.
746 (let ((name (intern (concat "cpp " color))))
747 (make-face name)
748 (set-face-background name color)
749 (cons color name)))
750
751(cpp-choose-default-face (if window-system cpp-face-type 'none))
752
753(defun cpp-face-name (face)
754 ;; Return the name of FACE from `cpp-face-all-list'.
755 (let ((entry (rassq (if face face 'default) cpp-face-all-list)))
756 (if entry
757 (car entry)
758 (format "<%s>" face))))
759
760;;; Utilities:
761
762(defvar cpp-progress-time 0)
763;; Last time we issued a progress message.
764
765(defun cpp-progress-message (&rest args)
766 ;; Report progress at most once a second. Take same ARGS as `message'.
767 (let ((time (nth 1 (current-time))))
768 (if (= time cpp-progress-time)
769 ()
770 (setq cpp-progress-time time)
771 (apply 'message args))))
772
773(provide 'cpp)
774
775;;; cpp.el ends here