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