1 ;;; hide-ifdef-mode.el --- hides selected code within ifdef.
3 ;;; Copyright (C) 1988, 1994 Free Software Foundation, Inc.
5 ;; Author: Dan LaLiberte <liberte@a.cs.uiuc.edu>
9 ;; This file is part of GNU Emacs.
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)
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.
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
23 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27 ;;; To initialize, toggle the hide-ifdef minor mode with
29 ;;; M-x hide-ifdef-mode
31 ;;; This will set up key bindings and call hide-ifdef-mode-hook if it
32 ;;; has a value. To explicitly hide ifdefs using a buffer-local
33 ;;; define list (default empty), type
35 ;;; M-x hide-ifdefs or C-c h
37 ;;; Hide-ifdef suppresses the display of code that the preprocessor wouldn't
38 ;;; pass through. The support of constant expressions in #if lines is
39 ;;; limited to identifiers, parens, and the operators: &&, ||, !, and
40 ;;; "defined". Please extend this.
42 ;;; The hidden code is marked by ellipses (...). Be
43 ;;; cautious when editing near ellipses, since the hidden text is
44 ;;; still in the buffer, and you can move the point into it and modify
45 ;;; text unawares. If you don't want to see the ellipses, set
46 ;;; selective-display-ellipses to nil. But this can be dangerous.
47 ;;; You can make your buffer read-only while hide-ifdef-hiding by setting
48 ;;; hide-ifdef-read-only to a non-nil value. You can toggle this
49 ;;; variable with hide-ifdef-toggle-read-only (C-c C-q).
51 ;;; You can undo the effect of hide-ifdefs by typing
53 ;;; M-x show-ifdefs or C-c s
55 ;;; Use M-x hide-ifdef-define (C-c d) to define a symbol.
56 ;;; Use M-x hide-ifdef-undef (C-c u) to undefine a symbol.
58 ;;; If you define or undefine a symbol while hide-ifdef-mode is in effect,
59 ;;; the display will be updated. Only the define list for the current
60 ;;; buffer will be affected. You can save changes to the local define
61 ;;; list with hide-ifdef-set-define-alist. This adds entries
62 ;;; to hide-ifdef-define-alist.
64 ;;; If you have defined a hide-ifdef-mode-hook, you can set
65 ;;; up a list of symbols that may be used by hide-ifdefs as in the
66 ;;; following example:
68 ;;; (setq hide-ifdef-mode-hook
70 ;;; (if (not hide-ifdef-define-alist)
71 ;;; (setq hide-ifdef-define-alist
75 ;;; (hide-ifdef-use-define-alist 'list2) ; use list2 by default
78 ;;; You can call hide-ifdef-use-define-alist (C-c u) at any time to specify
79 ;;; another list to use.
81 ;;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
82 ;;; set hide-ifdef-initially to non-nil.
84 ;;; If you set hide-ifdef-lines to t, hide-ifdefs hides all the #ifdef lines.
85 ;;; In the absence of highlighting, that might be a bad idea. If you set
86 ;;; hide-ifdef-lines to nil (the default), the surrounding preprocessor
87 ;;; lines will be displayed. That can be confusing in its own
88 ;;; right. Other variations on display are possible, but not much
91 ;;; You can explicitly hide or show individual ifdef blocks irrespective
92 ;;; of the define list by using hide-ifdef-block and show-ifdef-block.
94 ;;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
95 ;;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
97 ;;; If you have minor-mode-alist in your mode line (the default) two labels
98 ;;; may appear. "Ifdef" will appear when hide-ifdef-mode is active. "Hiding"
99 ;;; will appear when text may be hidden ("hide-ifdef-hiding" is non-nil).
101 ;;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
102 ;;; Extensively modified by Daniel LaLiberte (while at Gould).
104 ;;; You may freely modify and distribute this, but keep a record
105 ;;; of modifications and send comments to:
106 ;;; liberte@a.cs.uiuc.edu or ihnp4!uiucdcs!liberte
107 ;;; I will continue to upgrade hide-ifdef-mode
108 ;;; with your contributions.
112 ;;; Revision 1.7 88/02/16 03:12:58 liberte
113 ;;; Fixed comments and doc strings.
114 ;;; Added optional prefix arg for ifdef motion commands.
116 ;;; Revision 1.6 88/02/05 00:36:18 liberte
118 ;;; 1. A multi-line comment that starts on an #ifdef line
119 ;;; now ends on that line.
120 ;;; 2. Fix bad function name: hide-hif-ifdef-toggle-read-only
121 ;;; 3. Make ifdef-block hiding work outside of ifdefs.
123 ;;; Revision 1.5 88/01/31 23:19:31 liberte
125 ;;; Prefix internal names with "hif-".
127 ;;; Revision 1.4 88/01/30 14:09:38 liberte
128 ;;; Add hide-ifdef-hiding and hide-ifdef-mode to minor-mode-alist.
130 ;;; Revision 1.3 88/01/29 00:38:19 liberte
132 ;;; 1. Function "defined" is just like lookup.
133 ;;; 2. Skip to newline or cr in case text is hidden.
134 ;;; 3. Use car of token list if just one symbol.
136 ;;; Revision 1.2 88/01/28 23:32:46 liberte
137 ;;; Use hide-ifdef-mode-prefix-key.
138 ;;; Copy current-local-map so other buffers do not get
139 ;;; hide-ifdef-mode bindings.
144 (defvar hide-ifdef-mode-submap nil
145 "Keymap used with Hide-Ifdef mode.")
147 (defvar hide-ifdef-mode-map nil
148 "Keymap used with Hide-Ifdef mode.")
150 (defconst hide-ifdef-mode-prefix-key
"\C-c"
151 "Prefix key for all Hide-Ifdef mode commands.")
153 ;; Set up the submap that goes after the prefix key.
154 (if hide-ifdef-mode-submap
155 () ; dont redefine it.
156 (setq hide-ifdef-mode-submap
(make-sparse-keymap))
157 (define-key hide-ifdef-mode-submap
"\ed" 'hide-ifdef-define
)
158 (define-key hide-ifdef-mode-submap
"\eu" 'hide-ifdef-undef
)
159 (define-key hide-ifdef-mode-submap
"\eD" 'hide-ifdef-set-define-alist
)
160 (define-key hide-ifdef-mode-submap
"\eU" 'hide-ifdef-use-define-alist
)
162 (define-key hide-ifdef-mode-submap
"\eh" 'hide-ifdefs
)
163 (define-key hide-ifdef-mode-submap
"\es" 'show-ifdefs
)
164 (define-key hide-ifdef-mode-submap
"\C-d" 'hide-ifdef-block
)
165 (define-key hide-ifdef-mode-submap
"\C-s" 'show-ifdef-block
)
167 (define-key hide-ifdef-mode-submap
"\C-q" 'hide-ifdef-toggle-read-only
)
168 (let ((where (where-is-internal 'toggle-read-only
'(keymap) t
)))
170 (define-key hide-ifdef-mode-submap
172 'hide-ifdef-toggle-outside-read-only
)))
175 ;; Set up the mode's main map, which leads via the prefix key to the submap.
176 (if hide-ifdef-mode-map
178 (setq hide-ifdef-mode-map
(make-sparse-keymap))
179 (define-key hide-ifdef-mode-map hide-ifdef-mode-prefix-key
180 hide-ifdef-mode-submap
))
182 (defun hif-update-mode-line ()
183 "Update mode-line by setting buffer-modified to itself."
184 (set-buffer-modified-p (buffer-modified-p)))
186 (defvar hide-ifdef-mode nil
187 "Non-nil when hide-ifdef-mode is activated.")
189 (defvar hide-ifdef-hiding nil
190 "Non-nil when text may be hidden.")
192 ;; Arrange to use the mode's map when the mode is enabled.
193 (or (assq 'hide-ifdef-mode minor-mode-map-alist
)
194 (setq minor-mode-map-alist
195 (cons (cons hide-ifdef-mode hide-ifdef-mode-map
)
196 minor-mode-map-alist
)))
198 (or (assq 'hide-ifdef-hiding minor-mode-alist
)
199 (setq minor-mode-alist
200 (cons '(hide-ifdef-hiding " Hiding")
203 (or (assq 'hide-ifdef-mode minor-mode-alist
)
204 (setq minor-mode-alist
205 (cons '(hide-ifdef-mode " Ifdef")
209 (defun hide-ifdef-mode (arg)
210 "Toggle Hide-Ifdef mode. This is a minor mode, albeit a large one.
211 With ARG, turn Hide-Ifdef mode on iff arg is positive.
212 In Hide-Ifdef mode, code within #ifdef constructs that the C preprocessor
213 would eliminate may be hidden from view. Several variables affect
214 how the hiding is done:
217 An association list of defined and undefined symbols for the
218 current buffer. Initially, the global value of `hide-ifdef-env'
221 hide-ifdef-define-alist
222 An association list of defined symbol lists.
223 Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
224 and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
225 from one of the lists in `hide-ifdef-define-alist'.
228 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
229 #endif lines when hiding.
232 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
236 Set to non-nil if you want to make buffers read only while hiding.
237 After `show-ifdefs', read-only status is restored to previous value.
239 \\{hide-ifdef-mode-map}"
242 (make-local-variable 'hide-ifdef-mode
)
243 (setq hide-ifdef-mode
245 (not hide-ifdef-mode
)
246 (> (prefix-numeric-value arg
) 0)))
248 (force-mode-line-update)
252 ; fix c-mode syntax table so we can recognize whole symbols.
253 (modify-syntax-entry ?_
"w")
254 (modify-syntax-entry ?
& ".")
255 (modify-syntax-entry ?\|
".")
257 ; inherit global values
258 (make-local-variable 'hide-ifdef-env
)
259 (setq hide-ifdef-env
(default-value 'hide-ifdef-env
))
261 (make-local-variable 'hide-ifdef-hiding
)
262 (setq hide-ifdef-hiding
(default-value 'hide-ifdef-hiding
))
264 (make-local-variable 'hif-outside-read-only
)
265 (setq hif-outside-read-only buffer-read-only
)
267 (run-hooks 'hide-ifdef-mode-hook
)
269 (if hide-ifdef-initially
272 (message "Enter hide-ifdef-mode.")
274 ; else end hide-ifdef-mode
275 (if hide-ifdef-hiding
277 (message "Exit hide-ifdef-mode.")
281 ;; from outline.el with docstring fixed.
282 (defun hif-outline-flag-region (from to flag
)
283 "Hides or shows lines from FROM to TO, according to FLAG. If FLAG
284 is \\n (newline character) then text is shown, while if FLAG is \\^M
285 \(control-M) the text is hidden."
286 (let ((modp (buffer-modified-p)))
287 (unwind-protect (progn
288 (subst-char-in-region from to
289 (if (= flag ?
\n) ?\^M ?
\n)
291 (set-buffer-modified-p modp
))
294 (defun hif-show-all ()
295 "Show all of the text in the current buffer."
297 (hif-outline-flag-region (point-min) (point-max) ?
\n))
299 (defun hide-ifdef-region (start end
)
300 "START is the start of a #if or #else form. END is the ending part.
301 Everything including these lines is made invisible."
302 (hif-outline-flag-region start end ?\^M
)
305 (defun hif-show-ifdef-region (start end
)
306 "Everything between START and END is made visible."
307 (hif-outline-flag-region start end ?
\n)
312 ;===%%SF%% evaluation (Start) ===
314 (defvar hide-ifdef-evaluator
'eval
315 "The evaluator is given a canonical form and returns T if text under
316 that form should be displayed.")
318 (defvar hif-undefined-symbol nil
319 "...is by default considered to be false.")
321 (defvar hide-ifdef-env nil
322 "An alist of defined symbols and their values.")
325 (defun hif-set-var (var value
)
326 "Prepend (var value) pair to hide-ifdef-env."
327 (setq hide-ifdef-env
(cons (cons var value
) hide-ifdef-env
)))
330 (defun hif-lookup (var)
331 ; (message "hif-lookup %s" var)
332 (let ((val (assoc var hide-ifdef-env
)))
335 hif-undefined-symbol
)))
337 (defun hif-defined (var)
339 ; when #if expressions are fully supported, defined result should be 1
340 ; (if (assoc var hide-ifdef-env)
346 ;===%%SF%% evaluation (End) ===
350 ;===%%SF%% parsing (Start) ===
351 ;;; The code that understands what ifs and ifdef in files look like.
353 (defconst hif-cpp-prefix
"\\(^\\|\r\\)[ \t]*#[ \t]*")
354 (defconst hif-ifndef-regexp
(concat hif-cpp-prefix
"ifndef"))
355 (defconst hif-ifx-regexp
(concat hif-cpp-prefix
"if\\(n?def\\)?[ \t]+"))
356 (defconst hif-else-regexp
(concat hif-cpp-prefix
"else"))
357 (defconst hif-endif-regexp
(concat hif-cpp-prefix
"endif"))
358 (defconst hif-ifx-else-endif-regexp
359 (concat hif-ifx-regexp
"\\|" hif-else-regexp
"\\|" hif-endif-regexp
))
362 (defun hif-infix-to-prefix (token-list)
363 "Convert list of tokens in infix into prefix list"
364 ; (message "hif-infix-to-prefix: %s" token-list)
365 (if (= 1 (length token-list
))
366 (` (hif-lookup (quote (, (car token-list
)))))
367 (hif-parse-if-exp token-list
))
370 ; pattern to match initial identifier, !, &&, ||, (, or ).
371 (defconst hif-token-regexp
"^\\(!\\|&&\\|||\\|[()]\\|\\w+\\)")
372 (defconst hif-end-of-comment
"\\*/")
375 (defun hif-tokenize (expr-string)
376 "Separate string into a list of tokens"
377 (let ((token-list nil
)
379 (expr-length (length expr-string
)))
381 (while (< expr-start expr-length
)
382 ; (message "expr-start = %d" expr-start) (sit-for 1)
384 ((string-match "^[ \t]+" expr-string expr-start
)
386 (setq expr-start
(match-end 0))
387 ; stick newline in string so ^ matches on the next string-match
388 (aset expr-string
(1- expr-start
) ?
\n)
391 ((string-match "^/\\*" expr-string expr-start
)
392 (setq expr-start
(match-end 0))
393 (aset expr-string
(1- expr-start
) ?
\n)
395 (string-match hif-end-of-comment
396 expr-string expr-start
) ; eat comment
397 (string-match "$" expr-string expr-start
)) ; multi-line comment
398 (setq expr-start
(match-end 0))
399 (aset expr-string
(1- expr-start
) ?
\n)
402 ((string-match "^//" expr-string expr-start
)
403 (string-match "$" expr-string expr-start
)
404 (setq expr-start
(match-end 0))
407 ((string-match hif-token-regexp expr-string expr-start
)
408 (let ((token (substring expr-string expr-start
(match-end 0))))
409 (setq expr-start
(match-end 0))
410 (aset expr-string
(1- expr-start
) ?
\n)
411 ; (message "token: %s" token) (sit-for 1)
415 ((string-equal token
"||") 'or
)
416 ((string-equal token
"&&") 'and
)
417 ((string-equal token
"!") 'not
)
418 ((string-equal token
"defined") 'hif-defined
)
419 ((string-equal token
"(") 'lparen
)
420 ((string-equal token
")") 'rparen
)
424 (t (error "Bad #if expression: %s" expr-string
))
426 (nreverse token-list
)
429 ;;;-----------------------------------------------------------------
430 ;;; Translate C preprocessor #if expressions using recursive descent.
431 ;;; This parser is limited to the operators &&, ||, !, and "defined".
433 (defun hif-parse-if-exp (token-list)
434 "Parse the TOKEN-LIST. Return translated list in prefix form."
438 (if token
; is there still a token?
439 (error "Error: unexpected token: %s" token
))))
441 (defun hif-nexttoken ()
442 "Pop the next token from token-list into the let variable \"token\"."
443 (setq token
(car token-list
))
444 (setq token-list
(cdr token-list
))
448 "Parse and expression of the form
449 expr : term | expr '||' term."
450 (let ((result (hif-term)))
451 (while (eq token
'or
)
453 (setq result
(list 'or result
(hif-term))))
457 "Parse a term of the form
458 term : factor | term '&&' factor."
459 (let ((result (hif-factor)))
460 (while (eq token
'and
)
462 (setq result
(list 'and result
(hif-factor))))
466 "Parse a factor of the form
467 factor : '!' factor | '(' expr ')' | 'defined(' id ')' | id."
471 (list 'not
(hif-factor)))
475 (let ((result (hif-expr)))
476 (if (not (eq token
'rparen
))
477 (error "Bad token in parenthesized expression: %s" token
)
481 ((eq token
'hif-defined
)
483 (if (not (eq token
'lparen
))
484 (error "Error: expected \"(\" after \"defined\""))
487 (if (memq token
'(or and not hif-defined lparen rparen
))
488 (error "Error: unexpected token: %s" token
))
490 (if (not (eq token
'rparen
))
491 (error "Error: expected \")\" after identifier"))
493 (` (hif-defined (quote (, ident
))))
498 (if (memq ident
'(or and
))
499 (error "Error: missing identifier"))
501 (` (hif-lookup (quote (, ident
))))
505 ;;;----------- end of parser -----------------------
508 (defun hif-canonicalize ()
509 "When at beginning of #ifX, returns a canonical (evaluatable)
510 form for the expression."
512 (let ((negate (looking-at hif-ifndef-regexp
)))
513 (re-search-forward hif-ifx-regexp
)
515 (buffer-substring (point)
516 (progn (skip-chars-forward "^\n\r") (point))))
517 (expr (hif-infix-to-prefix (hif-tokenize expr-string
))))
518 ; (message "hif-canonicalized: %s" expr)
524 (defun hif-find-any-ifX ()
525 "Position at beginning of next #if, #ifdef, or #ifndef, including one on
527 ; (message "find ifX at %d" (point))
529 (re-search-forward hif-ifx-regexp
(point-max) t
)
530 (beginning-of-line)))
533 (defun hif-find-next-relevant ()
534 "Position at beginning of next #ifdef, #ifndef, #else, #endif,
535 NOT including one on this line."
536 ; (message "hif-find-next-relevant at %d" (point))
538 ; avoid infinite recursion by only going to beginning of line if match found
539 (if (re-search-forward hif-ifx-else-endif-regexp
(point-max) t
)
540 (beginning-of-line)))
542 (defun hif-find-previous-relevant ()
543 "Position at beginning of previous #ifdef, #ifndef, #else, #endif,
544 NOT including one on this line."
545 ; (message "hif-find-previous-relevant at %d" (point))
547 ; avoid infinite recursion by only going to beginning of line if match found
548 (if (re-search-backward hif-ifx-else-endif-regexp
(point-min) t
)
549 (beginning-of-line)))
552 (defun hif-looking-at-ifX () ;; Should eventually see #if
553 (looking-at hif-ifx-regexp
))
554 (defun hif-looking-at-endif ()
555 (looking-at hif-endif-regexp
))
556 (defun hif-looking-at-else ()
557 (looking-at hif-else-regexp
))
561 (defun hif-ifdef-to-endif ()
562 "If positioned at #ifX or #else form, skip to corresponding #endif."
563 ; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
564 (hif-find-next-relevant)
565 (cond ((hif-looking-at-ifX)
566 (hif-ifdef-to-endif) ; find endif of nested if
567 (hif-ifdef-to-endif)) ; find outer endif or else
568 ((hif-looking-at-else)
569 (hif-ifdef-to-endif)) ; find endif following else
570 ((hif-looking-at-endif)
573 (error "Mismatched #ifdef #endif pair"))))
576 (defun hif-endif-to-ifdef ()
577 "If positioned at #endif form, skip backward to corresponding #ifX."
578 ; (message "hif-endif-to-ifdef at %d" (point))
579 (let ((start (point)))
580 (hif-find-previous-relevant)
581 (if (= start
(point))
582 (error "Mismatched #ifdef #endif pair")))
583 (cond ((hif-looking-at-endif)
584 (hif-endif-to-ifdef) ; find beginning of nested if
585 (hif-endif-to-ifdef)) ; find beginning of outer if or else
586 ((hif-looking-at-else)
587 (hif-endif-to-ifdef))
588 ((hif-looking-at-ifX)
590 (t))) ; never gets here
593 (defun forward-ifdef (&optional arg
)
594 "Move point to beginning of line of the next ifdef-endif.
595 With argument, do this that many times."
597 (or arg
(setq arg
1))
599 (backward-ifdef (- arg
)))
602 (let ((start (point)))
603 (if (not (hif-looking-at-ifX))
604 (hif-find-next-relevant))
605 (if (hif-looking-at-ifX)
608 (error "No following #ifdef")
612 (defun backward-ifdef (&optional arg
)
613 "Move point to beginning of the previous ifdef-endif.
614 With argument, do this that many times."
616 (or arg
(setq arg
1))
618 (forward-ifdef (- arg
)))
622 (let ((start (point)))
623 (if (not (hif-looking-at-endif))
624 (hif-find-previous-relevant))
625 (if (hif-looking-at-endif)
628 (error "No previous #ifdef")))))
632 "Move point to beginning of nested ifdef or else-part."
634 (let ((start (point)))
635 (hif-find-next-relevant)
636 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
639 (error "No following #ifdef"))))
643 "Move point to beginning of enclosing ifdef or else-part."
646 (let ((start (point)))
647 (if (not (hif-looking-at-endif))
648 (hif-find-previous-relevant))
649 (if (hif-looking-at-endif)
650 (hif-endif-to-ifdef))
651 (if (= start
(point))
652 (error "No previous #ifdef"))))
654 (defun next-ifdef (&optional arg
)
655 "Move to the beginning of the next #ifX, #else, or #endif.
656 With argument, do this that many times."
658 (or arg
(setq arg
1))
660 (previous-ifdef (- arg
)))
663 (hif-find-next-relevant)
667 (error "No following #ifdefs, #elses, or #endifs")))))
669 (defun previous-ifdef (&optional arg
)
670 "Move to the beginning of the previous #ifX, #else, or #endif.
671 With argument, do this that many times."
673 (or arg
(setq arg
1))
675 (next-ifdef (- arg
)))
678 (let ((start (point)))
679 (hif-find-previous-relevant)
680 (if (= start
(point))
681 (error "No previous #ifdefs, #elses, or #endifs")
685 ;===%%SF%% parsing (End) ===
688 ;===%%SF%% hide-ifdef-hiding (Start) ===
691 ;;; A range is a structure with four components:
692 ;;; ELSE-P True if there was an else clause for the ifdef.
693 ;;; START The start of the range. (beginning of line)
694 ;;; ELSE The else marker (beginning of line)
695 ;;; Only valid if ELSE-P is true.
696 ;;; END The end of the range. (beginning of line)
698 (defun hif-make-range (else-p start end
&optional else
)
699 (list else-p start else end
))
701 (defun hif-range-else-p (range) (elt range
0))
702 (defun hif-range-start (range) (elt range
1))
703 (defun hif-range-else (range) (elt range
2))
704 (defun hif-range-end (range) (elt range
3))
709 ;;; The workhorse, it delimits the #if region. Reasonably simple:
710 ;;; Skip until an #else or #endif is found, remembering positions. If
711 ;;; an #else was found, skip some more, looking for the true #endif.
713 (defun hif-find-range ()
714 "Returns a Range structure describing the current #if region.
715 Point is left unchanged."
716 ; (message "hif-find-range at %d" (point))
719 (let ((start (point))
723 ;; Part one. Look for either #endif or #else.
724 ;; This loop-and-a-half dedicated to E. Dijkstra.
725 (hif-find-next-relevant)
726 (while (hif-looking-at-ifX) ; Skip nested ifdef
728 (hif-find-next-relevant))
729 ;; Found either a #else or an #endif.
730 (cond ((hif-looking-at-else)
734 (setq end
(point)) ; (save-excursion (end-of-line) (point))
736 ;; If found #else, look for #endif.
739 (hif-find-next-relevant)
740 (while (hif-looking-at-ifX) ; Skip nested ifdef
742 (hif-find-next-relevant))
743 (if (hif-looking-at-else)
744 (error "Found two elses in a row? Broken!"))
745 (setq end
(point)) ; (save-excursion (end-of-line) (point))
747 (hif-make-range else-p start end else
))))
751 ;;; NOTE: If there's an #ifdef at the beginning of the file, we can't
752 ;;; hide it. There's no previous newline to replace. If we added
753 ;;; one, we'd throw off all the counts. Feh.
755 (defun hif-hide-line (point)
756 "Hide the line containing point. Does nothing if `hide-ifdef-lines' is nil."
760 (let ((modp (buffer-modified-p)))
764 (if (not (= (point) 1))
765 (hide-ifdef-region (1- (point)) (point))))
766 (set-buffer-modified-p modp
))
771 ;;; Hif-Possibly-Hide
772 ;;; There are four cases. The #ifX expression is "taken" if it
773 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
774 ;;; inside the #ifdef would be included when the program was
777 ;;; Case 1: #ifX taken, and there's an #else.
778 ;;; The #else part must be hidden. The #if (then) part must be
779 ;;; processed for nested #ifX's.
780 ;;; Case 2: #ifX taken, and there's no #else.
781 ;;; The #if part must be processed for nested #ifX's.
782 ;;; Case 3: #ifX not taken, and there's an #else.
783 ;;; The #if part must be hidden. The #else part must be processed
785 ;;; Case 4: #ifX not taken, and there's no #else.
786 ;;; The #ifX part must be hidden.
788 ;;; Further processing is done by narrowing to the relevant region
789 ;;; and just recursively calling hide-ifdef-guts.
791 ;;; When hif-possibly-hide returns, point is at the end of the
792 ;;; possibly-hidden range.
794 (defun hif-recurse-on (start end
)
795 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
800 (narrow-to-region (point) end
)
803 (defun hif-possibly-hide ()
804 "Called at #ifX expression, this hides those parts that should be
805 hidden, according to judgement of `hide-ifdef-evaluator'."
806 ; (message "hif-possibly-hide") (sit-for 1)
807 (let ((test (hif-canonicalize))
808 (range (hif-find-range)))
809 ; (message "test = %s" test) (sit-for 1)
811 (hif-hide-line (hif-range-end range
))
812 (if (funcall hide-ifdef-evaluator test
)
813 (cond ((hif-range-else-p range
) ; case 1
814 (hif-hide-line (hif-range-else range
))
815 (hide-ifdef-region (hif-range-else range
)
816 (1- (hif-range-end range
)))
817 (hif-recurse-on (hif-range-start range
)
818 (hif-range-else range
)))
820 (hif-recurse-on (hif-range-start range
)
821 (hif-range-end range
))))
822 (cond ((hif-range-else-p range
) ; case 3
823 (hif-hide-line (hif-range-else range
))
824 (hide-ifdef-region (hif-range-start range
)
825 (1- (hif-range-else range
)))
826 (hif-recurse-on (hif-range-else range
)
827 (hif-range-end range
)))
829 (hide-ifdef-region (point)
830 (1- (hif-range-end range
))))
832 (hif-hide-line (hif-range-start range
)) ; Always hide start.
833 (goto-char (hif-range-end range
))
839 (defun hide-ifdef-guts ()
840 "Does the work of `hide-ifdefs', except for the work that's pointless
841 to redo on a recursive entry."
842 ; (message "hide-ifdef-guts")
844 (goto-char (point-min))
845 (while (hif-find-any-ifX)
846 (hif-possibly-hide))))
848 ;===%%SF%% hide-ifdef-hiding (End) ===
851 ;===%%SF%% exports (Start) ===
854 (defvar hide-ifdef-initially nil
855 "*Non-nil if `hide-ifdefs' should be called when Hide-Ifdef mode
856 is first activated.")
858 (defvar hide-ifdef-hiding nil
859 "Non-nil if text might be hidden.")
862 (defvar hide-ifdef-read-only nil
863 "*Set to non-nil if you want buffer to be read-only while hiding text.")
865 (defvar hif-outside-read-only nil
866 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
869 (defvar hide-ifdef-lines nil
870 "*Set to t if you don't want to see the #ifX, #else, and #endif lines.")
872 (defun hide-ifdef-toggle-read-only ()
873 "Toggle hide-ifdef-read-only."
875 (setq hide-ifdef-read-only
(not hide-ifdef-read-only
))
876 (message "Hide-Read-Only %s"
877 (if hide-ifdef-read-only
"ON" "OFF"))
878 (if hide-ifdef-hiding
879 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
)))
880 (hif-update-mode-line))
882 (defun hide-ifdef-toggle-outside-read-only ()
883 "Replacement for `toggle-read-only' within Hide Ifdef mode."
885 (setq hif-outside-read-only
(not hif-outside-read-only
))
886 (message "Read only %s"
887 (if hif-outside-read-only
"ON" "OFF"))
888 (setq buffer-read-only
889 (or (and hide-ifdef-hiding hide-ifdef-read-only
)
890 hif-outside-read-only
)
892 (hif-update-mode-line))
895 (defun hide-ifdef-define (var)
896 "Define a VAR so that #ifdef VAR would be included."
897 (interactive "SDefine what? ")
899 (if hide-ifdef-hiding
(hide-ifdefs)))
901 (defun hide-ifdef-undef (var)
902 "Undefine a VAR so that #ifdef VAR would not be included."
903 (interactive "SUndefine what? ")
904 (hif-set-var var nil
)
905 (if hide-ifdef-hiding
(hide-ifdefs)))
908 (defun hide-ifdefs ()
909 "Hide the contents of some #ifdefs.
910 Assume that defined symbols have been added to `hide-ifdef-env'.
911 The text hidden is the text that would not be included by the C
912 preprocessor if it were given the file with those symbols defined.
914 Turn off hiding by calling `show-ifdef'."
917 (message "Hiding...")
918 (if (not hide-ifdef-mode
)
919 (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
920 (if hide-ifdef-hiding
921 (show-ifdefs)) ; Otherwise, deep confusion.
922 (let ((inhibit-read-only t
))
923 (setq selective-display t
)
924 (setq hide-ifdef-hiding t
)
926 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
))
927 (message "Hiding done"))
930 (defun show-ifdefs ()
931 "Cancel the effects of `hide-ifdef'. The contents of all #ifdefs is shown."
933 (setq buffer-read-only hif-outside-read-only
)
934 (setq selective-display nil
) ; defaults
935 (let ((inhibit-read-only t
))
937 (setq hide-ifdef-hiding nil
))
940 (defun hif-find-ifdef-block ()
941 "Utility for hide and show `ifdef-block'.
942 Set top and bottom of ifdef block."
946 (if (not (or (hif-looking-at-else) (hif-looking-at-ifX)))
950 (setq max-bottom
(1- (point))))
953 (if (not (hif-looking-at-endif))
954 (hif-find-next-relevant))
955 (while (hif-looking-at-ifX)
957 (hif-find-next-relevant))
958 (setq bottom
(min max-bottom
(1- (point))))))
962 (defun hide-ifdef-block ()
963 "Hide the ifdef block (true or false part) enclosing or before the cursor."
965 (if (not hide-ifdef-mode
)
967 (setq selective-display t
)
968 (let (top bottom
(inhibit-read-only t
))
969 (hif-find-ifdef-block) ; set top and bottom - dynamic scoping
970 (hide-ifdef-region top bottom
)
974 (hif-hide-line (1+ bottom
))))
975 (setq hide-ifdef-hiding t
))
976 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
)))
979 (defun show-ifdef-block ()
980 "Show the ifdef block (true or false part) enclosing or before the cursor."
982 (let ((inhibit-read-only t
))
986 (hif-show-ifdef-region (1- (point)) (progn (end-of-line) (point))))
989 (hif-find-ifdef-block)
990 (hif-show-ifdef-region (1- top
) bottom
)))))
993 ;;; definition alist support
995 (defvar hide-ifdef-define-alist nil
996 "A global assoc list of pre-defined symbol lists")
998 (defun hif-compress-define-list (env)
999 "Compress the define list ENV into a list of defined symbols only."
1000 (let ((defs (mapcar '(lambda (arg)
1001 (if (hif-lookup (car arg
)) (car arg
)))
1006 (setq new-defs
(cons (car defs
) new-defs
)))
1007 (setq defs
(cdr defs
)))
1010 (defun hide-ifdef-set-define-alist (name)
1011 "Set the association for NAME to `hide-ifdef-env'."
1012 (interactive "SSet define list: ")
1013 (setq hide-ifdef-define-alist
1014 (cons (cons name
(hif-compress-define-list hide-ifdef-env
))
1015 hide-ifdef-define-alist
)))
1017 (defun hide-ifdef-use-define-alist (name)
1018 "Set `hide-ifdef-env' to the define list specified by NAME."
1019 (interactive "SUse define list: ")
1020 (let ((define-list (assoc name hide-ifdef-define-alist
)))
1022 (setq hide-ifdef-env
1023 (mapcar '(lambda (arg) (cons arg t
))
1025 (error "No define list for %s" name
))
1026 (if hide-ifdef-hiding
(hide-ifdefs))))
1028 ;;; hideif.el ends here