(c++-mode): Doc fix.
[bpt/emacs.git] / lisp / progmodes / cpp.el
... / ...
CommitLineData
1;;; cpp.el --- Highlight or hide text according to cpp conditionals.
2
3;; Copyright (C) 1994, 1995 Free Software Foundation
4
5;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
6;; Keywords: c, faces, tools
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
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.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
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.
19
20;; You should have received a copy of the GNU General Public License
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.
23
24;;; Commentary:
25
26;; Parse a text for C preprocessor conditionals, and highlight or hide
27;; the text inside the conditionals as you wish.
28
29;; This package is inspired by Jim Coplien's delta editor for SCCS.
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-config-file (convert-standard-filename ".cpp.el")
51 "*File name to save cpp configuration.")
52
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
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
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
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.
168A prefix arg suppresses display of that buffer."
169 (interactive "P")
170 (setq cpp-parse-symbols nil)
171 (cpp-parse-reset)
172 (if (null cpp-edit-list)
173 (cpp-edit-load))
174 (let (cpp-state-stack)
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")
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))
216 from to)))
217 (cpp-parse-close from to)
218 (setq cpp-state-stack (cons entry cpp-state-stack))))
219 ((string-equal type "endif")
220 (cpp-parse-close from to))
221 (t
222 (cpp-parse-error "Parser error"))))))))
223 (message "Parsing...done"))
224 (if cpp-state-stack
225 (save-excursion
226 (goto-char (nth 3 (car cpp-state-stack)))
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)
233 "Push information about conditional-beginning onto `cpp-state-stack'."
234 ;; Discard comments within this line.
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)))))
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.
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)))))
247 (setq cpp-state-stack (cons (list branch expr begin end) cpp-state-stack))
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)
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))
262 (if entry
263 (let ((face (nth (if branch 1 2) entry))
264 (read-only (eq (not branch) (nth 3 entry)))
265 (priority (length cpp-state-stack))
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
296 (cpp-highlight-buffer t))
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
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.
338 (overlay-put overlay 'modification-hooks '(cpp-signal-read-only))
339 (overlay-put overlay 'insert-in-front-hooks '(cpp-signal-read-only)))
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
352(defun cpp-signal-read-only (overlay after start end &optional len)
353 ;; Only allow deleting the whole overlay.
354 ;; Trying to change a read-only overlay.
355 (if (and (not after)
356 (or (< (overlay-start overlay) start)
357 (> (overlay-end overlay) end)))
358 (error "This text is read only")))
359
360(defun cpp-grow-overlay (overlay after start end &optional len)
361 ;; Make OVERLAY grow to contain range START to END.
362 (if after
363 (move-overlay overlay
364 (min start (overlay-start overlay))
365 (max end (overlay-end overlay)))))
366
367;;; Edit Buffer:
368
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
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 ()
416 "Major mode for editing the criteria for highlighting cpp conditionals.
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)
431 (cpp-highlight-buffer t))
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)
448 (insert "\n\nClick mouse-2 on item you want to change or use\n"
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
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
492 (if (> (length symbol) 39)
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)
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)))
516 (if (eq major-mode 'cpp-edit-mode)
517 (cpp-edit-reset)))
518
519(defun cpp-edit-save ()
520 "Load cpp configuration."
521 (interactive)
522 (require 'pp)
523 (save-excursion
524 (set-buffer cpp-edit-buffer)
525 (let ((buffer (find-file-noselect cpp-config-file)))
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)
540 (write-file cpp-config-file))))
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
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
628 cpp-callback-data
629 (completing-read "Symbol: " (mapcar 'list cpp-edit-symbols) nil t)))
630
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
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
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.
702 ;; DATA will be made available to CALLBACK
703 ;;in the free variable cpp-callback-data.
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))))
737 (let ((cpp-callback-data (get-text-property pos 'cpp-data))
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
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