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>
7 ;; Keywords: c, outlines
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 (defvar hide-ifdef-mode-submap nil
113 "Keymap used with Hide-Ifdef mode.")
115 (defvar hide-ifdef-mode-map nil
116 "Keymap used with Hide-Ifdef mode.")
118 (defconst hide-ifdef-mode-prefix-key
"\C-c@"
119 "Prefix key for all Hide-Ifdef mode commands.")
121 ;; Set up the submap that goes after the prefix key.
122 (if hide-ifdef-mode-submap
123 () ; dont redefine it.
124 (setq hide-ifdef-mode-submap
(make-sparse-keymap))
125 (define-key hide-ifdef-mode-submap
"d" 'hide-ifdef-define
)
126 (define-key hide-ifdef-mode-submap
"u" 'hide-ifdef-undef
)
127 (define-key hide-ifdef-mode-submap
"D" 'hide-ifdef-set-define-alist
)
128 (define-key hide-ifdef-mode-submap
"U" 'hide-ifdef-use-define-alist
)
130 (define-key hide-ifdef-mode-submap
"h" 'hide-ifdefs
)
131 (define-key hide-ifdef-mode-submap
"s" 'show-ifdefs
)
132 (define-key hide-ifdef-mode-submap
"\C-d" 'hide-ifdef-block
)
133 (define-key hide-ifdef-mode-submap
"\C-s" 'show-ifdef-block
)
135 (define-key hide-ifdef-mode-submap
"\C-q" 'hide-ifdef-toggle-read-only
)
136 (let ((where (where-is-internal 'toggle-read-only
'(keymap) t
)))
138 (define-key hide-ifdef-mode-submap
140 'hide-ifdef-toggle-outside-read-only
)))
143 ;; Set up the mode's main map, which leads via the prefix key to the submap.
144 (if hide-ifdef-mode-map
146 (setq hide-ifdef-mode-map
(make-sparse-keymap))
147 (define-key hide-ifdef-mode-map hide-ifdef-mode-prefix-key
148 hide-ifdef-mode-submap
))
150 (defvar hide-ifdef-mode nil
151 "Non-nil when hide-ifdef-mode is activated.")
153 (defvar hide-ifdef-hiding nil
154 "Non-nil when text may be hidden.")
156 ;; Arrange to use the mode's map when the mode is enabled.
157 (or (assq 'hide-ifdef-mode minor-mode-map-alist
)
158 (setq minor-mode-map-alist
159 (cons (cons 'hide-ifdef-mode hide-ifdef-mode-map
)
160 minor-mode-map-alist
)))
162 (or (assq 'hide-ifdef-hiding minor-mode-alist
)
163 (setq minor-mode-alist
164 (cons '(hide-ifdef-hiding " Hiding")
167 (or (assq 'hide-ifdef-mode minor-mode-alist
)
168 (setq minor-mode-alist
169 (cons '(hide-ifdef-mode " Ifdef")
172 ;; fix c-mode syntax table so we can recognize whole symbols.
173 (defvar hide-ifdef-syntax-table
174 (copy-syntax-table c-mode-syntax-table
)
175 "Syntax table used for tokenizing #if expressions.")
177 (modify-syntax-entry ?_
"w" hide-ifdef-syntax-table
)
178 (modify-syntax-entry ?
& "." hide-ifdef-syntax-table
)
179 (modify-syntax-entry ?\|
"." hide-ifdef-syntax-table
)
182 (defun hide-ifdef-mode (arg)
183 "Toggle Hide-Ifdef mode. This is a minor mode, albeit a large one.
184 With ARG, turn Hide-Ifdef mode on iff arg is positive.
185 In Hide-Ifdef mode, code within #ifdef constructs that the C preprocessor
186 would eliminate may be hidden from view. Several variables affect
187 how the hiding is done:
190 An association list of defined and undefined symbols for the
191 current buffer. Initially, the global value of `hide-ifdef-env'
194 hide-ifdef-define-alist
195 An association list of defined symbol lists.
196 Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
197 and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
198 from one of the lists in `hide-ifdef-define-alist'.
201 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
202 #endif lines when hiding.
205 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
209 Set to non-nil if you want to make buffers read only while hiding.
210 After `show-ifdefs', read-only status is restored to previous value.
212 \\{hide-ifdef-mode-map}"
215 (make-local-variable 'hide-ifdef-mode
)
216 (setq hide-ifdef-mode
218 (not hide-ifdef-mode
)
219 (> (prefix-numeric-value arg
) 0)))
221 (force-mode-line-update)
225 ; inherit global values
226 (make-local-variable 'hide-ifdef-env
)
227 (setq hide-ifdef-env
(default-value 'hide-ifdef-env
))
229 (make-local-variable 'hide-ifdef-hiding
)
230 (setq hide-ifdef-hiding
(default-value 'hide-ifdef-hiding
))
232 (make-local-variable 'hif-outside-read-only
)
233 (setq hif-outside-read-only buffer-read-only
)
235 (run-hooks 'hide-ifdef-mode-hook
)
237 (if hide-ifdef-initially
240 (message "Enter hide-ifdef-mode.")
242 ; else end hide-ifdef-mode
243 (if hide-ifdef-hiding
245 (message "Exit hide-ifdef-mode.")
249 ;; from outline.el with docstring fixed.
250 (defun hif-outline-flag-region (from to flag
)
251 "Hides or shows lines from FROM to TO, according to FLAG. If FLAG
252 is \\n (newline character) then text is shown, while if FLAG is \\^M
253 \(control-M) the text is hidden."
254 (let ((modp (buffer-modified-p)))
255 (unwind-protect (progn
256 (subst-char-in-region from to
257 (if (= flag ?
\n) ?\^M ?
\n)
259 (set-buffer-modified-p modp
))
262 (defun hif-show-all ()
263 "Show all of the text in the current buffer."
265 (hif-outline-flag-region (point-min) (point-max) ?
\n))
267 (defun hide-ifdef-region (start end
)
268 "START is the start of a #if or #else form. END is the ending part.
269 Everything including these lines is made invisible."
270 (hif-outline-flag-region start end ?\^M
)
273 (defun hif-show-ifdef-region (start end
)
274 "Everything between START and END is made visible."
275 (hif-outline-flag-region start end ?
\n)
280 ;===%%SF%% evaluation (Start) ===
282 (defvar hide-ifdef-evaluator
'eval
283 "The evaluator is given a canonical form and returns T if text under
284 that form should be displayed.")
286 (defvar hif-undefined-symbol nil
287 "...is by default considered to be false.")
289 (defvar hide-ifdef-env nil
290 "An alist of defined symbols and their values.")
293 (defun hif-set-var (var value
)
294 "Prepend (var value) pair to hide-ifdef-env."
295 (setq hide-ifdef-env
(cons (cons var value
) hide-ifdef-env
)))
298 (defun hif-lookup (var)
299 ; (message "hif-lookup %s" var)
300 (let ((val (assoc var hide-ifdef-env
)))
303 hif-undefined-symbol
)))
305 (defun hif-defined (var)
307 ; when #if expressions are fully supported, defined result should be 1
308 ; (if (assoc var hide-ifdef-env)
314 ;===%%SF%% evaluation (End) ===
318 ;===%%SF%% parsing (Start) ===
319 ;;; The code that understands what ifs and ifdef in files look like.
321 (defconst hif-cpp-prefix
"\\(^\\|\r\\)[ \t]*#[ \t]*")
322 (defconst hif-ifndef-regexp
(concat hif-cpp-prefix
"ifndef"))
323 (defconst hif-ifx-regexp
(concat hif-cpp-prefix
"if\\(n?def\\)?[ \t]+"))
324 (defconst hif-else-regexp
(concat hif-cpp-prefix
"else"))
325 (defconst hif-endif-regexp
(concat hif-cpp-prefix
"endif"))
326 (defconst hif-ifx-else-endif-regexp
327 (concat hif-ifx-regexp
"\\|" hif-else-regexp
"\\|" hif-endif-regexp
))
330 (defun hif-infix-to-prefix (token-list)
331 "Convert list of tokens in infix into prefix list"
332 ; (message "hif-infix-to-prefix: %s" token-list)
333 (if (= 1 (length token-list
))
334 (` (hif-lookup (quote (, (car token-list
)))))
335 (hif-parse-if-exp token-list
))
338 ; pattern to match initial identifier, !, &&, ||, (, or ).
339 ; Added ==, + and -: garyo@avs.com 8/9/94
340 (defconst hif-token-regexp
"^\\(!\\|&&\\|||\\|[!=]=\\|[()+-]\\|\\w+\\)")
341 (defconst hif-end-of-comment
"\\*/")
344 (defun hif-tokenize (expr-string)
345 "Separate string into a list of tokens"
346 (let ((token-list nil
)
348 (expr-length (length expr-string
))
349 (current-syntax-table (syntax-table)))
352 (set-syntax-table hide-ifdef-syntax-table
)
353 (while (< expr-start expr-length
)
354 ; (message "expr-start = %d" expr-start) (sit-for 1)
356 ((string-match "^[ \t]+" expr-string expr-start
)
358 (setq expr-start
(match-end 0))
359 ;; stick newline in string so ^ matches on the next string-match
360 (aset expr-string
(1- expr-start
) ?
\n))
362 ((string-match "^/\\*" expr-string expr-start
)
363 (setq expr-start
(match-end 0))
364 (aset expr-string
(1- expr-start
) ?
\n)
366 (string-match hif-end-of-comment
367 expr-string expr-start
) ; eat comment
368 (string-match "$" expr-string expr-start
)) ; multi-line comment
369 (setq expr-start
(match-end 0))
370 (aset expr-string
(1- expr-start
) ?
\n))
372 ((string-match "^//" expr-string expr-start
)
373 (string-match "$" expr-string expr-start
)
374 (setq expr-start
(match-end 0)))
376 ((string-match hif-token-regexp expr-string expr-start
)
377 (let ((token (substring expr-string expr-start
(match-end 0))))
378 (setq expr-start
(match-end 0))
379 (aset expr-string
(1- expr-start
) ?
\n)
380 ; (message "token: %s" token) (sit-for 1)
384 ((string-equal token
"||") 'or
)
385 ((string-equal token
"&&") 'and
)
386 ((string-equal token
"==") 'equal
)
387 ((string-equal token
"!=") 'hif-notequal
)
388 ((string-equal token
"!") 'not
)
389 ((string-equal token
"defined") 'hif-defined
)
390 ((string-equal token
"(") 'lparen
)
391 ((string-equal token
")") 'rparen
)
392 ((string-equal token
"+") 'hif-plus
)
393 ((string-equal token
"-") 'hif-minus
)
396 (t (error "Bad #if expression: %s" expr-string
)))))
397 (set-syntax-table current-syntax-table
))
398 (nreverse token-list
)))
400 ;;;-----------------------------------------------------------------
401 ;;; Translate C preprocessor #if expressions using recursive descent.
402 ;;; This parser is limited to the operators &&, ||, !, and "defined".
403 ;;; Added ==, !=, +, and -. Gary Oberbrunner, garyo@avs.com, 8/9/94
405 (defun hif-parse-if-exp (token-list)
406 "Parse the TOKEN-LIST. Return translated list in prefix form."
410 (if token
; is there still a token?
411 (error "Error: unexpected token: %s" token
))))
413 (defun hif-nexttoken ()
414 "Pop the next token from token-list into the let variable \"token\"."
415 (setq token
(car token-list
))
416 (setq token-list
(cdr token-list
))
420 "Parse and expression of the form
421 expr : term | expr '||' term."
422 (let ((result (hif-term)))
423 (while (eq token
'or
)
425 (setq result
(list 'or result
(hif-term))))
429 "Parse a term of the form
430 term : eq-expr | term '&&' eq-expr."
431 (let ((result (hif-eq-expr)))
432 (while (eq token
'and
)
434 (setq result
(list 'and result
(hif-eq-expr))))
437 (defun hif-eq-expr ()
438 "Parse a term of the form
439 eq-expr : math | eq-expr '=='|'!=' math."
440 (let ((result (hif-math))
442 (while (or (eq token
'equal
) (eq token
'hif-notequal
))
443 (setq eq-token token
)
445 (setq result
(list eq-token result
(hif-math))))
449 "Parse an expression of the form
450 math : factor | math '+|-' factor."
451 (let ((result (hif-factor))
453 (while (or (eq token
'hif-plus
) (eq token
'hif-minus
))
456 (setq result
(list math-op result
(hif-factor))))
460 "Parse a factor of the form
461 factor : '!' factor | '(' expr ')' | 'defined(' id ')' | id."
465 (list 'not
(hif-factor)))
469 (let ((result (hif-expr)))
470 (if (not (eq token
'rparen
))
471 (error "Bad token in parenthesized expression: %s" token
)
475 ((eq token
'hif-defined
)
477 (if (not (eq token
'lparen
))
478 (error "Error: expected \"(\" after \"defined\""))
481 (if (memq token
'(or and not hif-defined lparen rparen
))
482 (error "Error: unexpected token: %s" token
))
484 (if (not (eq token
'rparen
))
485 (error "Error: expected \")\" after identifier"))
487 (` (hif-defined (quote (, ident
))))
492 (if (memq ident
'(or and
))
493 (error "Error: missing identifier"))
495 (` (hif-lookup (quote (, ident
))))
499 (defun hif-mathify (val)
500 "Treat VAL as a number: if it's t or nil, use 1 or 0."
507 (defun hif-plus (a b
)
508 "Like ordinary plus but treat t and nil as 1 and 0."
509 (+ (hif-mathify a
) (hif-mathify b
)))
510 (defun hif-minus (a b
)
511 "Like ordinary minus but treat t and nil as 1 and 0."
512 (- (hif-mathify a
) (hif-mathify b
)))
513 (defun hif-notequal (a b
)
514 "Like (not (equal A B)) but as one symbol."
517 ;;;----------- end of parser -----------------------
520 (defun hif-canonicalize ()
521 "When at beginning of #ifX, returns a canonical (evaluatable)
522 form for the expression."
524 (let ((negate (looking-at hif-ifndef-regexp
)))
525 (re-search-forward hif-ifx-regexp
)
527 (buffer-substring (point)
528 (progn (skip-chars-forward "^\n\r") (point))))
529 (expr (hif-infix-to-prefix (hif-tokenize expr-string
))))
530 ; (message "hif-canonicalized: %s" expr)
536 (defun hif-find-any-ifX ()
537 "Position at beginning of next #if, #ifdef, or #ifndef, including one on
539 ; (message "find ifX at %d" (point))
541 (re-search-forward hif-ifx-regexp
(point-max) t
)
542 (beginning-of-line)))
545 (defun hif-find-next-relevant ()
546 "Position at beginning of next #ifdef, #ifndef, #else, #endif,
547 NOT including one on this line."
548 ; (message "hif-find-next-relevant at %d" (point))
550 ; avoid infinite recursion by only going to beginning of line if match found
551 (if (re-search-forward hif-ifx-else-endif-regexp
(point-max) t
)
552 (beginning-of-line)))
554 (defun hif-find-previous-relevant ()
555 "Position at beginning of previous #ifdef, #ifndef, #else, #endif,
556 NOT including one on this line."
557 ; (message "hif-find-previous-relevant at %d" (point))
559 ; avoid infinite recursion by only going to beginning of line if match found
560 (if (re-search-backward hif-ifx-else-endif-regexp
(point-min) t
)
561 (beginning-of-line)))
564 (defun hif-looking-at-ifX () ;; Should eventually see #if
565 (looking-at hif-ifx-regexp
))
566 (defun hif-looking-at-endif ()
567 (looking-at hif-endif-regexp
))
568 (defun hif-looking-at-else ()
569 (looking-at hif-else-regexp
))
573 (defun hif-ifdef-to-endif ()
574 "If positioned at #ifX or #else form, skip to corresponding #endif."
575 ; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
576 (hif-find-next-relevant)
577 (cond ((hif-looking-at-ifX)
578 (hif-ifdef-to-endif) ; find endif of nested if
579 (hif-ifdef-to-endif)) ; find outer endif or else
580 ((hif-looking-at-else)
581 (hif-ifdef-to-endif)) ; find endif following else
582 ((hif-looking-at-endif)
585 (error "Mismatched #ifdef #endif pair"))))
588 (defun hif-endif-to-ifdef ()
589 "If positioned at #endif form, skip backward to corresponding #ifX."
590 ; (message "hif-endif-to-ifdef at %d" (point))
591 (let ((start (point)))
592 (hif-find-previous-relevant)
593 (if (= start
(point))
594 (error "Mismatched #ifdef #endif pair")))
595 (cond ((hif-looking-at-endif)
596 (hif-endif-to-ifdef) ; find beginning of nested if
597 (hif-endif-to-ifdef)) ; find beginning of outer if or else
598 ((hif-looking-at-else)
599 (hif-endif-to-ifdef))
600 ((hif-looking-at-ifX)
602 (t))) ; never gets here
605 (defun forward-ifdef (&optional arg
)
606 "Move point to beginning of line of the next ifdef-endif.
607 With argument, do this that many times."
609 (or arg
(setq arg
1))
611 (backward-ifdef (- arg
)))
614 (let ((start (point)))
615 (if (not (hif-looking-at-ifX))
616 (hif-find-next-relevant))
617 (if (hif-looking-at-ifX)
620 (error "No following #ifdef")
624 (defun backward-ifdef (&optional arg
)
625 "Move point to beginning of the previous ifdef-endif.
626 With argument, do this that many times."
628 (or arg
(setq arg
1))
630 (forward-ifdef (- arg
)))
634 (let ((start (point)))
635 (if (not (hif-looking-at-endif))
636 (hif-find-previous-relevant))
637 (if (hif-looking-at-endif)
640 (error "No previous #ifdef")))))
644 "Move point to beginning of nested ifdef or else-part."
646 (let ((start (point)))
647 (hif-find-next-relevant)
648 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
651 (error "No following #ifdef"))))
655 "Move point to beginning of enclosing ifdef or else-part."
658 (let ((start (point)))
659 (if (not (hif-looking-at-endif))
660 (hif-find-previous-relevant))
661 (if (hif-looking-at-endif)
662 (hif-endif-to-ifdef))
663 (if (= start
(point))
664 (error "No previous #ifdef"))))
666 (defun next-ifdef (&optional arg
)
667 "Move to the beginning of the next #ifX, #else, or #endif.
668 With argument, do this that many times."
670 (or arg
(setq arg
1))
672 (previous-ifdef (- arg
)))
675 (hif-find-next-relevant)
679 (error "No following #ifdefs, #elses, or #endifs")))))
681 (defun previous-ifdef (&optional arg
)
682 "Move to the beginning of the previous #ifX, #else, or #endif.
683 With argument, do this that many times."
685 (or arg
(setq arg
1))
687 (next-ifdef (- arg
)))
690 (let ((start (point)))
691 (hif-find-previous-relevant)
692 (if (= start
(point))
693 (error "No previous #ifdefs, #elses, or #endifs")
697 ;===%%SF%% parsing (End) ===
700 ;===%%SF%% hide-ifdef-hiding (Start) ===
703 ;;; A range is a structure with four components:
704 ;;; ELSE-P True if there was an else clause for the ifdef.
705 ;;; START The start of the range. (beginning of line)
706 ;;; ELSE The else marker (beginning of line)
707 ;;; Only valid if ELSE-P is true.
708 ;;; END The end of the range. (beginning of line)
710 (defun hif-make-range (else-p start end
&optional else
)
711 (list else-p start else end
))
713 (defun hif-range-else-p (range) (elt range
0))
714 (defun hif-range-start (range) (elt range
1))
715 (defun hif-range-else (range) (elt range
2))
716 (defun hif-range-end (range) (elt range
3))
721 ;;; The workhorse, it delimits the #if region. Reasonably simple:
722 ;;; Skip until an #else or #endif is found, remembering positions. If
723 ;;; an #else was found, skip some more, looking for the true #endif.
725 (defun hif-find-range ()
726 "Returns a Range structure describing the current #if region.
727 Point is left unchanged."
728 ; (message "hif-find-range at %d" (point))
731 (let ((start (point))
735 ;; Part one. Look for either #endif or #else.
736 ;; This loop-and-a-half dedicated to E. Dijkstra.
737 (hif-find-next-relevant)
738 (while (hif-looking-at-ifX) ; Skip nested ifdef
740 (hif-find-next-relevant))
741 ;; Found either a #else or an #endif.
742 (cond ((hif-looking-at-else)
746 (setq end
(point)) ; (save-excursion (end-of-line) (point))
748 ;; If found #else, look for #endif.
751 (hif-find-next-relevant)
752 (while (hif-looking-at-ifX) ; Skip nested ifdef
754 (hif-find-next-relevant))
755 (if (hif-looking-at-else)
756 (error "Found two elses in a row? Broken!"))
757 (setq end
(point)) ; (save-excursion (end-of-line) (point))
759 (hif-make-range else-p start end else
))))
763 ;;; NOTE: If there's an #ifdef at the beginning of the file, we can't
764 ;;; hide it. There's no previous newline to replace. If we added
765 ;;; one, we'd throw off all the counts. Feh.
767 (defun hif-hide-line (point)
768 "Hide the line containing point. Does nothing if `hide-ifdef-lines' is nil."
772 (let ((modp (buffer-modified-p)))
776 (if (not (= (point) 1))
777 (hide-ifdef-region (1- (point)) (point))))
778 (set-buffer-modified-p modp
))
783 ;;; Hif-Possibly-Hide
784 ;;; There are four cases. The #ifX expression is "taken" if it
785 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
786 ;;; inside the #ifdef would be included when the program was
789 ;;; Case 1: #ifX taken, and there's an #else.
790 ;;; The #else part must be hidden. The #if (then) part must be
791 ;;; processed for nested #ifX's.
792 ;;; Case 2: #ifX taken, and there's no #else.
793 ;;; The #if part must be processed for nested #ifX's.
794 ;;; Case 3: #ifX not taken, and there's an #else.
795 ;;; The #if part must be hidden. The #else part must be processed
797 ;;; Case 4: #ifX not taken, and there's no #else.
798 ;;; The #ifX part must be hidden.
800 ;;; Further processing is done by narrowing to the relevant region
801 ;;; and just recursively calling hide-ifdef-guts.
803 ;;; When hif-possibly-hide returns, point is at the end of the
804 ;;; possibly-hidden range.
806 (defun hif-recurse-on (start end
)
807 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
812 (narrow-to-region (point) end
)
815 (defun hif-possibly-hide ()
816 "Called at #ifX expression, this hides those parts that should be
817 hidden, according to judgement of `hide-ifdef-evaluator'."
818 ; (message "hif-possibly-hide") (sit-for 1)
819 (let ((test (hif-canonicalize))
820 (range (hif-find-range)))
821 ; (message "test = %s" test) (sit-for 1)
823 (hif-hide-line (hif-range-end range
))
824 (if (funcall hide-ifdef-evaluator test
)
825 (cond ((hif-range-else-p range
) ; case 1
826 (hif-hide-line (hif-range-else range
))
827 (hide-ifdef-region (hif-range-else range
)
828 (1- (hif-range-end range
)))
829 (hif-recurse-on (hif-range-start range
)
830 (hif-range-else range
)))
832 (hif-recurse-on (hif-range-start range
)
833 (hif-range-end range
))))
834 (cond ((hif-range-else-p range
) ; case 3
835 (hif-hide-line (hif-range-else range
))
836 (hide-ifdef-region (hif-range-start range
)
837 (1- (hif-range-else range
)))
838 (hif-recurse-on (hif-range-else range
)
839 (hif-range-end range
)))
841 (hide-ifdef-region (point)
842 (1- (hif-range-end range
))))
844 (hif-hide-line (hif-range-start range
)) ; Always hide start.
845 (goto-char (hif-range-end range
))
851 (defun hide-ifdef-guts ()
852 "Does the work of `hide-ifdefs', except for the work that's pointless
853 to redo on a recursive entry."
854 ; (message "hide-ifdef-guts")
856 (goto-char (point-min))
857 (while (hif-find-any-ifX)
858 (hif-possibly-hide))))
860 ;===%%SF%% hide-ifdef-hiding (End) ===
863 ;===%%SF%% exports (Start) ===
866 (defvar hide-ifdef-initially nil
867 "*Non-nil if `hide-ifdefs' should be called when Hide-Ifdef mode
868 is first activated.")
871 (defvar hide-ifdef-read-only nil
872 "*Set to non-nil if you want buffer to be read-only while hiding text.")
874 (defvar hif-outside-read-only nil
875 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
878 (defvar hide-ifdef-lines nil
879 "*Set to t if you don't want to see the #ifX, #else, and #endif lines.")
881 (defun hide-ifdef-toggle-read-only ()
882 "Toggle hide-ifdef-read-only."
884 (setq hide-ifdef-read-only
(not hide-ifdef-read-only
))
885 (message "Hide-Read-Only %s"
886 (if hide-ifdef-read-only
"ON" "OFF"))
887 (if hide-ifdef-hiding
888 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
)))
889 (force-mode-line-update))
891 (defun hide-ifdef-toggle-outside-read-only ()
892 "Replacement for `toggle-read-only' within Hide Ifdef mode."
894 (setq hif-outside-read-only
(not hif-outside-read-only
))
895 (message "Read only %s"
896 (if hif-outside-read-only
"ON" "OFF"))
897 (setq buffer-read-only
898 (or (and hide-ifdef-hiding hide-ifdef-read-only
)
899 hif-outside-read-only
)
901 (force-mode-line-update))
904 (defun hide-ifdef-define (var)
905 "Define a VAR so that #ifdef VAR would be included."
906 (interactive "SDefine what? ")
908 (if hide-ifdef-hiding
(hide-ifdefs)))
910 (defun hide-ifdef-undef (var)
911 "Undefine a VAR so that #ifdef VAR would not be included."
912 (interactive "SUndefine what? ")
913 (hif-set-var var nil
)
914 (if hide-ifdef-hiding
(hide-ifdefs)))
917 (defun hide-ifdefs ()
918 "Hide the contents of some #ifdefs.
919 Assume that defined symbols have been added to `hide-ifdef-env'.
920 The text hidden is the text that would not be included by the C
921 preprocessor if it were given the file with those symbols defined.
923 Turn off hiding by calling `show-ifdefs'."
926 (message "Hiding...")
927 (if (not hide-ifdef-mode
)
928 (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
929 (if hide-ifdef-hiding
930 (show-ifdefs)) ; Otherwise, deep confusion.
931 (let ((inhibit-read-only t
))
932 (setq selective-display t
)
933 (setq hide-ifdef-hiding t
)
935 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
))
936 (message "Hiding done"))
939 (defun show-ifdefs ()
940 "Cancel the effects of `hide-ifdef'. The contents of all #ifdefs is shown."
942 (setq buffer-read-only hif-outside-read-only
)
943 (setq selective-display nil
) ; defaults
944 (let ((inhibit-read-only t
))
946 (setq hide-ifdef-hiding nil
))
949 (defun hif-find-ifdef-block ()
950 "Utility for hide and show `ifdef-block'.
951 Set top and bottom of ifdef block."
955 (if (not (or (hif-looking-at-else) (hif-looking-at-ifX)))
959 (setq max-bottom
(1- (point))))
962 (if (not (hif-looking-at-endif))
963 (hif-find-next-relevant))
964 (while (hif-looking-at-ifX)
966 (hif-find-next-relevant))
967 (setq bottom
(min max-bottom
(1- (point))))))
971 (defun hide-ifdef-block ()
972 "Hide the ifdef block (true or false part) enclosing or before the cursor."
974 (if (not hide-ifdef-mode
)
976 (setq selective-display t
)
977 (let (top bottom
(inhibit-read-only t
))
978 (hif-find-ifdef-block) ; set top and bottom - dynamic scoping
979 (hide-ifdef-region top bottom
)
983 (hif-hide-line (1+ bottom
))))
984 (setq hide-ifdef-hiding t
))
985 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
)))
988 (defun show-ifdef-block ()
989 "Show the ifdef block (true or false part) enclosing or before the cursor."
991 (let ((inhibit-read-only t
))
995 (hif-show-ifdef-region (1- (point)) (progn (end-of-line) (point))))
998 (hif-find-ifdef-block)
999 (hif-show-ifdef-region (1- top
) bottom
)))))
1002 ;;; definition alist support
1004 (defvar hide-ifdef-define-alist nil
1005 "A global assoc list of pre-defined symbol lists")
1007 (defun hif-compress-define-list (env)
1008 "Compress the define list ENV into a list of defined symbols only."
1009 (let ((defs (mapcar '(lambda (arg)
1010 (if (hif-lookup (car arg
)) (car arg
)))
1015 (setq new-defs
(cons (car defs
) new-defs
)))
1016 (setq defs
(cdr defs
)))
1019 (defun hide-ifdef-set-define-alist (name)
1020 "Set the association for NAME to `hide-ifdef-env'."
1021 (interactive "SSet define list: ")
1022 (setq hide-ifdef-define-alist
1023 (cons (cons name
(hif-compress-define-list hide-ifdef-env
))
1024 hide-ifdef-define-alist
)))
1026 (defun hide-ifdef-use-define-alist (name)
1027 "Set `hide-ifdef-env' to the define list specified by NAME."
1028 (interactive "SUse define list: ")
1029 (let ((define-list (assoc name hide-ifdef-define-alist
)))
1031 (setq hide-ifdef-env
1032 (mapcar '(lambda (arg) (cons arg t
))
1034 (error "No define list for %s" name
))
1035 (if hide-ifdef-hiding
(hide-ifdefs))))
1037 ;;; hideif.el ends here