comparison lisp/modes/hideif.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; hide-ifdef-mode.el --- hides selected code within ifdef.
2 ;; Keywords: c
3
4 ;;; Copyright (C) 1988 Brian Marick and Daniel LaLiberte
5 ;;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
6 ;;; Extensively modified by Daniel LaLiberte (while at Gould).
7 ;;;
8 ;;; You may freely modify and distribute this, but keep a record
9 ;;; of modifications and send comments to:
10 ;;; liberte@a.cs.uiuc.edu or ihnp4!uiucdcs!liberte
11 ;;; I will continue to upgrade hide-ifdef-mode
12 ;;; with your contributions and will eventually offer it to FSF.
13
14 ;;; Revision 1.7 88/02/16 03:12:58 liberte
15 ;;; Fixed comments and doc strings.
16 ;;; Added optional prefix arg for ifdef motion commands.
17 ;;;
18 ;;; Revision 1.6 88/02/05 00:36:18 liberte
19 ;;; Bug fixes.
20 ;;; 1. A multi-line comment that starts on an #ifdef line
21 ;;; now ends on that line.
22 ;;; 2. Fix bad function name: hide-hif-ifdef-toggle-read-only
23 ;;; 3. Make ifdef-block hiding work outside of ifdefs.
24 ;;;
25 ;;; Revision 1.5 88/01/31 23:19:31 liberte
26 ;;; Major clean up.
27 ;;; Prefix internal names with "hif-".
28 ;;;
29 ;;; Revision 1.4 88/01/30 14:09:38 liberte
30 ;;; Add hide-ifdef-hiding and hide-ifdef-mode to minor-mode-alist.
31 ;;;
32 ;;; Revision 1.3 88/01/29 00:38:19 liberte
33 ;;; Fix three bugs.
34 ;;; 1. Function "defined" is just like lookup.
35 ;;; 2. Skip to newline or cr in case text is hidden.
36 ;;; 3. Use car of token list if just one symbol.
37 ;;;
38 ;;; Revision 1.2 88/01/28 23:32:46 liberte
39 ;;; Use hide-ifdef-mode-prefix-key.
40 ;;; Copy current-local-map so other buffers do not get
41 ;;; hide-ifdef-mode bindings.
42 ;;;
43 ;;;--------------------------------------------------------------
44
45 ;;; Commentary:
46
47 ;;; To initialize, toggle the hide-ifdef minor mode with
48 ;;;
49 ;;; M-x hide-ifdef-mode
50 ;;;
51 ;;; This will set up key bindings and call hide-ifdef-mode-hook if it
52 ;;; has a value. To explicitly hide ifdefs using a buffer-local
53 ;;; define list (default empty), type
54 ;;;
55 ;;; M-x hide-ifdefs or C-c h
56 ;;;
57 ;;; Hide-ifdef suppresses the display of code that the preprocessor wouldn't
58 ;;; pass through. The support of constant expressions in #if lines is
59 ;;; limited to identifiers, parens, and the operators: &&, ||, !, and
60 ;;; "defined". Please extend this.
61 ;;;
62 ;;; The hidden code is marked by ellipses (...). Be
63 ;;; cautious when editing near ellipses, since the hidden text is
64 ;;; still in the buffer, and you can move the point into it and modify
65 ;;; text unawares. If you don't want to see the ellipses, set
66 ;;; selective-display-ellipses to nil. But this can be dangerous.
67 ;;; You can make your buffer read-only while hide-ifdef-hiding by setting
68 ;;; hide-ifdef-read-only to a non-nil value. You can toggle this
69 ;;; variable with hide-ifdef-toggle-read-only (C-c C-q).
70 ;;;
71 ;;; You can undo the effect of hide-ifdefs by typing
72 ;;;
73 ;;; M-x show-ifdefs or C-c s
74 ;;;
75 ;;; Use M-x hide-ifdef-define (C-c d) to define a symbol.
76 ;;; Use M-x hide-ifdef-undef (C-c u) to undefine a symbol.
77 ;;;
78 ;;; If you define or undefine a symbol while hide-ifdef-mode is in effect,
79 ;;; the display will be updated. Only the define list for the current
80 ;;; buffer will be affected. You can save changes to the local define
81 ;;; list with hide-ifdef-set-define-alist. This adds entries
82 ;;; to hide-ifdef-define-alist.
83 ;;;
84 ;;; If you have defined a hide-ifdef-mode-hook, you can set
85 ;;; up a list of symbols that may be used by hide-ifdefs as in the
86 ;;; following example:
87 ;;;
88 ;;; (setq hide-ifdef-mode-hook
89 ;;; '(lambda ()
90 ;;; (if (not hide-ifdef-define-alist)
91 ;;; (setq hide-ifdef-define-alist
92 ;;; '((list1 ONE TWO)
93 ;;; (list2 TWO THREE)
94 ;;; )))
95 ;;; (hide-ifdef-use-define-alist 'list2) ; use list2 by default
96 ;;; ))
97 ;;;
98 ;;; You can call hide-ifdef-use-define-alist (C-c u) at any time to specify
99 ;;; another list to use.
100 ;;;
101 ;;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
102 ;;; set hide-ifdef-initially to non-nil.
103 ;;;
104 ;;; If you set hide-ifdef-lines to t, hide-ifdefs hides all the #ifdef lines.
105 ;;; In the absence of highlighting, that might be a bad idea. If you set
106 ;;; hide-ifdef-lines to nil (the default), the surrounding preprocessor
107 ;;; lines will be displayed. That can be confusing in its own
108 ;;; right. Other variations on display are possible, but not much
109 ;;; better.
110 ;;;
111 ;;; You can explicitly hide or show individual ifdef blocks irrespective
112 ;;; of the define list by using hide-ifdef-block and show-ifdef-block.
113 ;;;
114 ;;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
115 ;;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
116 ;;;
117 ;;; If you have minor-mode-alist in your modeline (the default) two labels
118 ;;; may appear. "Ifdef" will appear when hide-ifdef-mode is active. "Hiding"
119 ;;; will appear when text may be hidden ("hide-ifdef-hiding" is non-nil).
120
121 ;;; Code:
122
123 (defvar hide-ifdef-mode-map nil
124 "Keymap used with hide-ifdef mode")
125
126 (defconst hide-ifdef-mode-prefix-key "\C-c"
127 "Prefix key for all hide-ifdef-mode commands.")
128
129 (defvar hide-ifdef-mode-map-before nil
130 "Buffer-local variable to store a copy of the local keymap
131 before hide-ifdef-mode modifies it.")
132
133 (defun define-hide-ifdef-mode-map ()
134 (if hide-ifdef-mode-map
135 () ; dont redefine it.
136 (setq hide-ifdef-mode-map (make-sparse-keymap))
137 (define-key hide-ifdef-mode-map "d" 'hide-ifdef-define)
138 (define-key hide-ifdef-mode-map "u" 'hide-ifdef-undef)
139 (define-key hide-ifdef-mode-map "D" 'hide-ifdef-set-define-alist)
140 (define-key hide-ifdef-mode-map "U" 'hide-ifdef-use-define-alist)
141
142 (define-key hide-ifdef-mode-map "h" 'hide-ifdefs)
143 (define-key hide-ifdef-mode-map "s" 'show-ifdefs)
144 (define-key hide-ifdef-mode-map "\C-h" 'hide-ifdef-block)
145 (define-key hide-ifdef-mode-map "\C-s" 'show-ifdef-block)
146
147 (define-key hide-ifdef-mode-map "\C-f" 'forward-ifdef)
148 (define-key hide-ifdef-mode-map "\C-b" 'backward-ifdef)
149 (define-key hide-ifdef-mode-map "\C-d" 'down-ifdef)
150 (define-key hide-ifdef-mode-map "\C-u" 'up-ifdef)
151 (define-key hide-ifdef-mode-map "\C-n" 'next-ifdef)
152 (define-key hide-ifdef-mode-map "\C-p" 'previous-ifdef)
153 (define-key hide-ifdef-mode-map "\C-q" 'hide-ifdef-toggle-read-only)
154 (define-key hide-ifdef-mode-map
155 (where-is-internal 'toggle-read-only nil t)
156 'hide-ifdef-toggle-outside-read-only)
157 )
158 (fset 'hide-ifdef-mode-map hide-ifdef-mode-map) ; the function is the map
159 )
160
161 (defun hif-update-mode-line ()
162 "Update mode-line by setting buffer-modified to itself."
163 (set-buffer-modified-p (buffer-modified-p)))
164
165
166 (defvar hide-ifdef-mode nil
167 "non-nil when hide-ifdef-mode is activated.")
168
169 (defvar hide-ifdef-hiding nil
170 "non-nil when text may be hidden.")
171
172 (or (assq 'hide-ifdef-hiding minor-mode-alist)
173 (setq minor-mode-alist
174 (cons '(hide-ifdef-hiding " Hiding")
175 minor-mode-alist)))
176
177 ;(or (assq 'hide-ifdef-mode minor-mode-alist)
178 ; (setq minor-mode-alist
179 ; (cons '(hide-ifdef-mode " Ifdef")
180 ; minor-mode-alist)))
181 ;; XEmacs: do it right.
182 ;;;###autoload
183 (add-minor-mode 'hide-ifdef-mode " Ifdef")
184
185 (defun hide-ifdef-mode (arg)
186 "Toggle hide-ifdef-mode. Thus this is a minor mode, albeit a large one.
187 With arg, turn hide-ifdef-mode on iff arg is positive.
188 In hide-ifdef-mode, code within #ifdef constructs that the C preprocessor
189 would eliminate may be hidden from view. Several variables affect
190 how the hiding is done:
191
192 hide-ifdef-env
193 An association list of defined and undefined symbols for the
194 current buffer. Initially, the global value of hide-ifdef-env is used.
195
196 hide-ifdef-define-alist
197 An association list of defined symbol lists.
198 Use hide-ifdef-set-define-alist to save the current hide-ifdef-env
199 and hide-ifdef-use-define-alist to set the current hide-ifdef-env
200 from one of the lists in hide-ifdef-define-alist.
201
202 hide-ifdef-lines
203 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
204 #endif lines when hiding.
205
206 hide-ifdef-initially
207 Indicates whether hide-ifdefs should be called when hide-ifdef-mode
208 is activated.
209
210 hide-ifdef-read-only
211 Set to non-nil if you want to make buffers read only while hiding.
212 After show-ifdefs, read-only status is restored to previous value.
213
214 \\{hide-ifdef-mode-map}"
215
216 (interactive "P")
217 (make-local-variable 'hide-ifdef-mode)
218 (setq hide-ifdef-mode
219 (if (null arg)
220 (not hide-ifdef-mode)
221 (> (prefix-numeric-value arg) 0)))
222
223 (hif-update-mode-line)
224
225 (if hide-ifdef-mode
226 (progn
227 ; fix c-mode syntax table so we can recognize whole symbols.
228 (modify-syntax-entry ?_ "w")
229 (modify-syntax-entry ?& ".")
230 (modify-syntax-entry ?\| ".")
231
232 ; inherit global values
233 (make-local-variable 'hide-ifdef-env)
234 (setq hide-ifdef-env (default-value 'hide-ifdef-env))
235
236 (make-local-variable 'hide-ifdef-hiding)
237 (setq hide-ifdef-hiding (default-value 'hide-ifdef-hiding))
238
239 (make-local-variable 'hif-outside-read-only)
240 (setq hif-outside-read-only buffer-read-only)
241
242 (make-local-variable 'hide-ifdef-mode-map-before)
243 (setq hide-ifdef-mode-map-before (current-local-map))
244 (use-local-map (copy-keymap (current-local-map)))
245 (local-unset-key hide-ifdef-mode-prefix-key)
246 (local-set-key hide-ifdef-mode-prefix-key 'hide-ifdef-mode-map)
247 (define-hide-ifdef-mode-map)
248
249 (run-hooks 'hide-ifdef-mode-hook)
250
251 (if hide-ifdef-initially
252 (hide-ifdefs)
253 (show-ifdefs))
254 (message "Enter hide-ifdef-mode.")
255 )
256 ; else end hide-ifdef-mode
257 (if hide-ifdef-hiding
258 (show-ifdefs))
259 (use-local-map hide-ifdef-mode-map-before)
260 (message "Exit hide-ifdef-mode.")
261 ))
262
263
264 ;; from outline.el with docstring fixed.
265 (defun hif-outline-flag-region (from to flag)
266 "Hides or shows lines from FROM to TO, according to FLAG. If FLAG
267 is \\n (newline character) then text is shown, while if FLAG is \\^M
268 \(control-M) the text is hidden."
269 (let ((modp (buffer-modified-p)))
270 (unwind-protect (progn
271 (subst-char-in-region from to
272 (if (= flag ?\n) ?\^M ?\n)
273 flag t) )
274 (set-buffer-modified-p modp))
275 ))
276
277 (defun hif-show-all ()
278 "Show all of the text in the current buffer."
279 (interactive)
280 (hif-outline-flag-region (point-min) (point-max) ?\n))
281
282 (defun hide-ifdef-region (start end)
283 "START is the start of a #if or #else form. END is the ending part.
284 Everything including these lines is made invisible."
285 (hif-outline-flag-region start end ?\^M)
286 )
287
288 (defun hif-show-ifdef-region (start end)
289 "Everything between START and END is made visible."
290 (hif-outline-flag-region start end ?\n)
291 )
292
293
294
295 ;===%%SF%% evaluation (Start) ===
296
297 (defvar hide-ifdef-evaluator 'eval
298 "The evaluator is given a canonical form and returns T if text under
299 that form should be displayed.")
300
301 (defvar hif-undefined-symbol nil
302 "...is by default considered to be false.")
303
304 (defvar hide-ifdef-env nil
305 "An alist of defined symbols and their values.")
306
307
308 (defun hif-set-var (var value)
309 "Prepend (var value) pair to hide-ifdef-env."
310 (setq hide-ifdef-env (cons (cons var value) hide-ifdef-env)))
311
312
313 (defun hif-lookup (var)
314 ; (message "hif-lookup %s" var)
315 (let ((val (assoc var hide-ifdef-env)))
316 (if val
317 (cdr val)
318 hif-undefined-symbol)))
319
320 (defun hif-defined (var)
321 (hif-lookup var)
322 ; when #if expressions are fully supported, defined result should be 1
323 ; (if (assoc var hide-ifdef-env)
324 ; 1
325 ; nil)
326 )
327
328
329 ;===%%SF%% evaluation (End) ===
330
331
332
333 ;===%%SF%% parsing (Start) ===
334 ;;; The code that understands what ifs and ifdef in files look like.
335
336 (defconst hif-cpp-prefix "\\(^\\|\r\\)[ \t]*#[ \t]*")
337 (defconst hif-ifndef-regexp (concat hif-cpp-prefix "ifndef"))
338 (defconst hif-ifx-regexp (concat hif-cpp-prefix "if\\(n?def\\)?[ \t]+"))
339 (defconst hif-else-regexp (concat hif-cpp-prefix "else"))
340 (defconst hif-endif-regexp (concat hif-cpp-prefix "endif"))
341 (defconst hif-ifx-else-endif-regexp
342 (concat hif-ifx-regexp "\\|" hif-else-regexp "\\|" hif-endif-regexp))
343
344
345 (defun hif-infix-to-prefix (token-list)
346 "Convert list of tokens in infix into prefix list"
347 ; (message "hif-infix-to-prefix: %s" token-list)
348 (if (= 1 (length token-list))
349 (` (hif-lookup (quote (, (car token-list)))))
350 (hif-parse-if-exp token-list))
351 )
352
353 ; pattern to match initial identifier, !, &&, ||, (, or ).
354 (defconst hif-token-regexp "^\\(!\\|&&\\|||\\|[()]\\|\\w+\\)")
355 (defconst hif-end-of-comment "\\*/")
356
357
358 (defun hif-tokenize (expr-string)
359 "Separate string into a list of tokens"
360 (let ((token-list nil)
361 (expr-start 0)
362 (expr-length (length expr-string)))
363
364 (while (< expr-start expr-length)
365 ; (message "expr-start = %d" expr-start) (sit-for 1)
366 (cond
367 ((string-match "^[ \t]+" expr-string expr-start)
368 ; skip whitespace
369 (setq expr-start (match-end 0))
370 ; stick newline in string so ^ matches on the next string-match
371 (aset expr-string (1- expr-start) ?\n)
372 )
373
374 ((string-match "^/\\*" expr-string expr-start)
375 (setq expr-start (match-end 0))
376 (aset expr-string (1- expr-start) ?\n)
377 (or
378 (string-match hif-end-of-comment
379 expr-string expr-start) ; eat comment
380 (string-match "$" expr-string expr-start)) ; multi-line comment
381 (setq expr-start (match-end 0))
382 (aset expr-string (1- expr-start) ?\n)
383 )
384
385 ((string-match hif-token-regexp expr-string expr-start)
386 (let ((token (substring expr-string expr-start (match-end 0))))
387 (setq expr-start (match-end 0))
388 (aset expr-string (1- expr-start) ?\n)
389 ; (message "token: %s" token) (sit-for 1)
390 (setq token-list
391 (cons
392 (cond
393 ((string-equal token "||") 'or)
394 ((string-equal token "&&") 'and)
395 ((string-equal token "!") 'not)
396 ((string-equal token "defined") 'hif-defined)
397 ((string-equal token "(") 'lparen)
398 ((string-equal token ")") 'rparen)
399 (t (intern token)))
400 token-list))
401 ))
402 (t (error "Bad #if expression: %s" expr-string))
403 ))
404 (nreverse token-list)
405 ))
406
407 ;;;-----------------------------------------------------------------
408 ;;; Translate C preprocessor #if expressions using recursive descent.
409 ;;; This parser is limited to the operators &&, ||, !, and "defined".
410
411 (defun hif-parse-if-exp (token-list)
412 "Parse the TOKEN-LIST. Return translated list in prefix form."
413 (hif-nexttoken)
414 (prog1
415 (hif-expr)
416 (if token ; is there still a token?
417 (error "Error: unexpected token: %s" token)))
418 )
419
420 (defun hif-nexttoken ()
421 "Pop the next token from token-list into the let variable \"token\"."
422 (setq token (car token-list))
423 (setq token-list (cdr token-list))
424 token
425 )
426
427 (defun hif-expr ()
428 "Parse and expression of the form
429 expr : term | expr '||' term."
430 (let ((result (hif-term)))
431 (while (eq token 'or)
432 (hif-nexttoken)
433 (setq result (list 'or result (hif-term))))
434 result
435 ))
436
437 (defun hif-term ()
438 "Parse a term of the form
439 term : factor | term '&&' factor."
440 (let ((result (hif-factor)))
441 (while (eq token 'and)
442 (hif-nexttoken)
443 (setq result (list 'and result (hif-factor))))
444 result
445 ))
446
447 (defun hif-factor ()
448 "Parse a factor of the form
449 factor : '!' factor | '(' expr ')' | 'defined(' id ')' | id."
450 (cond
451 ((eq token 'not)
452 (hif-nexttoken)
453 (list 'not (hif-factor)))
454
455 ((eq token 'lparen)
456 (hif-nexttoken)
457 (let ((result (hif-expr)))
458 (if (not (eq token 'rparen))
459 (error "Bad token in parenthesized expression: %s" token)
460 (hif-nexttoken)
461 result)))
462
463 ((eq token 'hif-defined)
464 (hif-nexttoken)
465 (if (not (eq token 'lparen))
466 (error "Error: expected \"(\" after \"defined\""))
467 (hif-nexttoken)
468 (let ((ident token))
469 (if (memq token '(or and not hif-defined lparen rparen))
470 (error "Error: unexpected token: %s" token))
471 (hif-nexttoken)
472 (if (not (eq token 'rparen))
473 (error "Error: expected \")\" after identifier"))
474 (hif-nexttoken)
475 (` (hif-defined (quote (, ident))))
476 ))
477
478 (t ; identifier
479 (let ((ident token))
480 (if (memq ident '(or and))
481 (error "Error: missing identifier"))
482 (hif-nexttoken)
483 (` (hif-lookup (quote (, ident))))
484 ))
485
486 ))
487
488 ;;;----------- end of parser -----------------------
489
490
491 (defun hif-canonicalize ()
492 "When at beginning of #ifX, returns a canonical (evaluatable)
493 form for the expression."
494 (save-excursion
495 (let ((negate (looking-at hif-ifndef-regexp)))
496 (re-search-forward hif-ifx-regexp)
497 (let* ((expr-string
498 (buffer-substring (point)
499 (progn (skip-chars-forward "^\n\r") (point))))
500 (expr (hif-infix-to-prefix (hif-tokenize expr-string))))
501 ; (message "hif-canonicalized: %s" expr)
502 (if negate
503 (list 'not expr)
504 expr)))))
505
506
507 (defun hif-find-any-ifX ()
508 "Position at beginning of next #if, #ifdef, or #ifndef, including one on
509 this line."
510 ; (message "find ifX at %d" (point))
511 (prog1
512 (re-search-forward hif-ifx-regexp (point-max) t)
513 (beginning-of-line)))
514
515
516 (defun hif-find-next-relevant ()
517 "Position at beginning of next #ifdef, #ifndef, #else, #endif,
518 NOT including one on this line."
519 ; (message "hif-find-next-relevant at %d" (point))
520 (end-of-line)
521 ; avoid infinite recursion by only going to beginning of line if match found
522 (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
523 (beginning-of-line))
524 )
525
526 (defun hif-find-previous-relevant ()
527 "Position at beginning of previous #ifdef, #ifndef, #else, #endif,
528 NOT including one on this line."
529 ; (message "hif-find-previous-relevant at %d" (point))
530 (beginning-of-line)
531 ; avoid infinite recursion by only going to beginning of line if match found
532 (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
533 (beginning-of-line)
534 )
535 )
536
537
538 (defun hif-looking-at-ifX () ;; Should eventually see #if
539 (looking-at hif-ifx-regexp))
540 (defun hif-looking-at-endif ()
541 (looking-at hif-endif-regexp))
542 (defun hif-looking-at-else ()
543 (looking-at hif-else-regexp))
544
545
546
547 (defun hif-ifdef-to-endif ()
548 "If positioned at #ifX or #else form, skip to corresponding #endif."
549 ; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
550 (hif-find-next-relevant)
551 (cond ((hif-looking-at-ifX)
552 (hif-ifdef-to-endif) ; find endif of nested if
553 (hif-ifdef-to-endif)) ; find outer endif or else
554 ((hif-looking-at-else)
555 (hif-ifdef-to-endif)) ; find endif following else
556 ((hif-looking-at-endif)
557 'done)
558 (t
559 (error "Missmatched #ifdef #endif pair"))
560 ))
561
562
563 (defun hif-endif-to-ifdef ()
564 "If positioned at #endif form, skip backward to corresponding #ifX."
565 ; (message "hif-endif-to-ifdef at %d" (point))
566 (let ((start (point)))
567 (hif-find-previous-relevant)
568 (if (= start (point))
569 (error "Missmatched #ifdef #endif pair")))
570 (cond ((hif-looking-at-endif)
571 (hif-endif-to-ifdef) ; find beginning of nested if
572 (hif-endif-to-ifdef)) ; find beginning of outer if or else
573 ((hif-looking-at-else)
574 (hif-endif-to-ifdef))
575 ((hif-looking-at-ifX)
576 'done)
577 (t ; never gets here
578 )))
579
580
581 (defun forward-ifdef (&optional arg)
582 "Move point to beginning of line of the next ifdef-endif.
583 With argument, do this that many times."
584 (interactive "p")
585 (or arg (setq arg 1))
586 (if (< arg 0)
587 (backward-ifdef (- arg)))
588 (while (< 0 arg)
589 (setq arg (- arg))
590 (let ((start (point)))
591 (if (not (hif-looking-at-ifX))
592 (hif-find-next-relevant))
593 (if (hif-looking-at-ifX)
594 (hif-ifdef-to-endif)
595 (goto-char start)
596 (error "No following #ifdef")
597 ))))
598
599
600 (defun backward-ifdef (&optional arg)
601 "Move point to beginning of the previous ifdef-endif.
602 With argument, do this that many times."
603 (interactive "p")
604 (or arg (setq arg 1))
605 (if (< arg 0)
606 (forward-ifdef (- arg)))
607 (while (< 0 arg)
608 (setq arg (1- arg))
609 (beginning-of-line)
610 (let ((start (point)))
611 (if (not (hif-looking-at-endif))
612 (hif-find-previous-relevant))
613 (if (hif-looking-at-endif)
614 (hif-endif-to-ifdef)
615 (goto-char start)
616 (error "No previous #ifdef")
617 ))))
618
619
620
621 (defun down-ifdef ()
622 "Move point to beginning of nested ifdef or else-part."
623 (interactive)
624 (let ((start (point)))
625 (hif-find-next-relevant)
626 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
627 ()
628 (goto-char start)
629 (error "No following #ifdef")
630 )))
631
632
633 (defun up-ifdef ()
634 "Move point to beginning of enclosing ifdef or else-part."
635 (interactive)
636 (beginning-of-line)
637 (let ((start (point)))
638 (if (not (hif-looking-at-endif))
639 (hif-find-previous-relevant))
640 (if (hif-looking-at-endif)
641 (hif-endif-to-ifdef))
642 (if (= start (point))
643 (error "No previous #ifdef")
644 )))
645
646 (defun next-ifdef (&optional arg)
647 "Move to the beginning of the next #ifX, #else, or #endif.
648 With argument, do this that many times."
649 (interactive "p")
650 (or arg (setq arg 1))
651 (if (< arg 0)
652 (previous-ifdef (- arg)))
653 (while (< 0 arg)
654 (setq arg (1- arg))
655 (hif-find-next-relevant)
656 (if (eolp)
657 (progn
658 (beginning-of-line)
659 (error "No following #ifdefs, #elses, or #endifs")
660 ))))
661
662 (defun previous-ifdef (&optional arg)
663 "Move to the beginning of the previous #ifX, #else, or #endif.
664 With argument, do this that many times."
665 (interactive "p")
666 (or arg (setq arg 1))
667 (if (< arg 0)
668 (next-ifdef (- arg)))
669 (while (< 0 arg)
670 (setq arg (1- arg))
671 (let ((start (point)))
672 (hif-find-previous-relevant)
673 (if (= start (point))
674 (error "No previous #ifdefs, #elses, or #endifs")
675 ))))
676
677
678 ;===%%SF%% parsing (End) ===
679
680
681 ;===%%SF%% hide-ifdef-hiding (Start) ===
682
683
684 ;;; A range is a structure with four components:
685 ;;; ELSE-P True if there was an else clause for the ifdef.
686 ;;; START The start of the range. (beginning of line)
687 ;;; ELSE The else marker (beginning of line)
688 ;;; Only valid if ELSE-P is true.
689 ;;; END The end of the range. (beginning of line)
690
691 (defun hif-make-range (else-p start end &optional else)
692 (list else-p start else end))
693
694 (defun hif-range-else-p (range) (elt range 0))
695 (defun hif-range-start (range) (elt range 1))
696 (defun hif-range-else (range) (elt range 2))
697 (defun hif-range-end (range) (elt range 3))
698
699
700
701 ;;; Find-Range
702 ;;; The workhorse, it delimits the #if region. Reasonably simple:
703 ;;; Skip until an #else or #endif is found, remembering positions. If
704 ;;; an #else was found, skip some more, looking for the true #endif.
705
706 (defun hif-find-range ()
707 "Returns a Range structure describing the current #if region.
708 Point is left unchanged."
709 ; (message "hif-find-range at %d" (point))
710 (save-excursion
711 (beginning-of-line)
712 (let ((start (point))
713 (else-p nil)
714 (else nil)
715 (end nil))
716 ;; Part one. Look for either #endif or #else.
717 ;; This loop-and-a-half dedicated to E. Dijkstra.
718 (hif-find-next-relevant)
719 (while (hif-looking-at-ifX) ; Skip nested ifdef
720 (hif-ifdef-to-endif)
721 (hif-find-next-relevant))
722 ;; Found either a #else or an #endif.
723 (cond ((hif-looking-at-else)
724 (setq else-p t)
725 (setq else (point)))
726 (t
727 (setq end (point)) ; (save-excursion (end-of-line) (point))
728 ))
729 ;; If found #else, look for #endif.
730 (if else-p
731 (progn
732 (hif-find-next-relevant)
733 (while (hif-looking-at-ifX) ; Skip nested ifdef
734 (hif-ifdef-to-endif)
735 (hif-find-next-relevant))
736 (if (hif-looking-at-else)
737 (error "Found two elses in a row? Broken!"))
738 (setq end (point)) ; (save-excursion (end-of-line) (point))
739 ))
740 (hif-make-range else-p start end else))))
741
742
743 ;;; A bit slimy.
744 ;;; NOTE: If there's an #ifdef at the beginning of the file, we can't
745 ;;; hide it. There's no previous newline to replace. If we added
746 ;;; one, we'd throw off all the counts. Feh.
747
748 (defun hif-hide-line (point)
749 "Hide the line containing point. Does nothing if
750 hide-ifdef-lines is nil."
751 (if hide-ifdef-lines
752 (save-excursion
753 (goto-char point)
754 (let ((modp (buffer-modified-p)))
755 (unwind-protect
756 (progn
757 (beginning-of-line)
758 (if (not (= (point) 1))
759 (hide-ifdef-region (1- (point)) (point))))
760 (set-buffer-modified-p modp))
761 ))
762 ))
763
764
765 ;;; Hif-Possibly-Hide
766 ;;; There are four cases. The #ifX expression is "taken" if it
767 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
768 ;;; inside the #ifdef would be included when the program was
769 ;;; compiled.
770 ;;;
771 ;;; Case 1: #ifX taken, and there's an #else.
772 ;;; The #else part must be hidden. The #if (then) part must be
773 ;;; processed for nested #ifX's.
774 ;;; Case 2: #ifX taken, and there's no #else.
775 ;;; The #if part must be processed for nested #ifX's.
776 ;;; Case 3: #ifX not taken, and there's an #else.
777 ;;; The #if part must be hidden. The #else part must be processed
778 ;;; for nested #ifs.
779 ;;; Case 4: #ifX not taken, and there's no #else.
780 ;;; The #ifX part must be hidden.
781 ;;;
782 ;;; Further processing is done by narrowing to the relevant region
783 ;;; and just recursively calling hide-ifdef-guts.
784 ;;;
785 ;;; When hif-possibly-hide returns, point is at the end of the
786 ;;; possibly-hidden range.
787
788 (defun hif-recurse-on (start end)
789 "Call hide-ifdef-guts after narrowing to end of START line and END
790 line."
791 (save-excursion
792 (save-restriction
793 (goto-char start)
794 (end-of-line)
795 (narrow-to-region (point) end)
796 (hide-ifdef-guts))))
797
798 (defun hif-possibly-hide ()
799 "Called at #ifX expression, this hides those parts that should be
800 hidden, according to judgement of hide-ifdef-evaluator."
801 ; (message "hif-possibly-hide") (sit-for 1)
802 (let ((test (hif-canonicalize))
803 (range (hif-find-range)))
804 ; (message "test = %s" test) (sit-for 1)
805
806 (hif-hide-line (hif-range-end range))
807 (if (funcall hide-ifdef-evaluator test)
808 (cond ((hif-range-else-p range) ; case 1
809 (hif-hide-line (hif-range-else range))
810 (hide-ifdef-region (hif-range-else range)
811 (1- (hif-range-end range)))
812 (hif-recurse-on (hif-range-start range)
813 (hif-range-else range)))
814 (t ; case 2
815 (hif-recurse-on (hif-range-start range)
816 (hif-range-end range))))
817 (cond ((hif-range-else-p range) ; case 3
818 (hif-hide-line (hif-range-else range))
819 (hide-ifdef-region (hif-range-start range)
820 (1- (hif-range-else range)))
821 (hif-recurse-on (hif-range-else range)
822 (hif-range-end range)))
823 (t ; case 4
824 (hide-ifdef-region (point)
825 (1- (hif-range-end range))))
826 ))
827 (hif-hide-line (hif-range-start range)) ; Always hide start.
828 (goto-char (hif-range-end range))
829 (end-of-line)
830 ))
831
832
833
834 (defun hide-ifdef-guts ()
835 "Does the work of hide-ifdefs, except for the work that's pointless
836 to redo on a recursive entry."
837 ; (message "hide-ifdef-guts")
838 (save-excursion
839 (goto-char (point-min))
840 (while (hif-find-any-ifX)
841 (hif-possibly-hide))))
842
843 ;===%%SF%% hide-ifdef-hiding (End) ===
844
845
846 ;===%%SF%% exports (Start) ===
847
848 (defvar hide-ifdef-initially nil
849 "*Non-nil if hide-ifdefs should be called when hide-ifdef-mode
850 is first activated.")
851
852 (defvar hide-ifdef-hiding nil
853 "Non-nil if text might be hidden.")
854
855 (defvar hide-ifdef-read-only nil
856 "*Set to non-nil if you want buffer to be read-only while hiding text.")
857
858 (defvar hif-outside-read-only nil
859 "Internal variable. Saves the value of buffer-read-only while hiding.")
860
861 (defvar hide-ifdef-lines nil
862 "*Set to t if you don't want to see the #ifX, #else, and #endif lines.")
863
864 (defun hide-ifdef-toggle-read-only ()
865 "Toggle hide-ifdef-read-only."
866 (interactive)
867 (setq hide-ifdef-read-only (not hide-ifdef-read-only))
868 (message "Hide-Read-Only %s"
869 (if hide-ifdef-read-only "ON" "OFF"))
870 (if hide-ifdef-hiding
871 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
872 (hif-update-mode-line)
873 )
874
875 (defun hide-ifdef-toggle-outside-read-only ()
876 "Replacement for toggle-read-only within hide-ifdef-mode."
877 (interactive)
878 (setq hif-outside-read-only (not hif-outside-read-only))
879 (message "Read only %s"
880 (if hif-outside-read-only "ON" "OFF"))
881 (setq buffer-read-only
882 (or (and hide-ifdef-hiding hide-ifdef-read-only)
883 hif-outside-read-only)
884 )
885 (hif-update-mode-line)
886 )
887
888
889 (defun hide-ifdef-define (var)
890 "Define a VAR so that #ifdef VAR would be included."
891 (interactive "SDefine what? ")
892 (hif-set-var var t)
893 (if hide-ifdef-hiding (hide-ifdefs)))
894
895 (defun hide-ifdef-undef (var)
896 "Undefine a VAR so that #ifdef VAR would not be included."
897 (interactive "SUndefine what? ")
898 (hif-set-var var nil)
899 (if hide-ifdef-hiding (hide-ifdefs)))
900
901
902 (defun hide-ifdefs ()
903 "Hide the contents of some #ifdefs. Assume that defined symbols have
904 been added to hide-ifdef-env. The text hidden is the text that would not
905 be included by the C preprocessor if it were given the file with those
906 symbols defined.
907
908 Turn off hiding by calling show-ifdef."
909
910 (interactive)
911 (message "Hiding...")
912 (if (not hide-ifdef-mode)
913 (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
914 (if hide-ifdef-hiding
915 (show-ifdefs)) ; Otherwise, deep confusion.
916 (if buffer-read-only (toggle-read-only)) ; make it writable temporarily
917 (setq selective-display t)
918 (setq hide-ifdef-hiding t)
919 (hide-ifdef-guts)
920 (if (or hide-ifdef-read-only hif-outside-read-only)
921 (toggle-read-only) ; make it read only
922 )
923 (message "Hiding done")
924 )
925
926
927 (defun show-ifdefs ()
928 "Cancel the effects of hide-ifdef. The contents of all #ifdefs is shown."
929 (interactive)
930 (if buffer-read-only (toggle-read-only)) ; make it writable temporarily
931 (setq selective-display nil) ; defaults
932 (hif-show-all)
933 (if hif-outside-read-only
934 (toggle-read-only)) ; make it read only
935 (setq hide-ifdef-hiding nil)
936 )
937
938
939 (defun hif-find-ifdef-block ()
940 "Utilitiy for hide and show ifdef-block. Set top and bottom of ifdef block."
941 (let (max-bottom)
942 (save-excursion
943 (beginning-of-line)
944 (if (not (or (hif-looking-at-else) (hif-looking-at-ifX)))
945 (up-ifdef))
946 (setq top (point))
947 (hif-ifdef-to-endif)
948 (setq max-bottom (1- (point)))
949 )
950 (save-excursion
951 (beginning-of-line)
952 (if (not (hif-looking-at-endif))
953 (hif-find-next-relevant))
954 (while (hif-looking-at-ifX)
955 (hif-ifdef-to-endif)
956 (hif-find-next-relevant)
957 )
958 (setq bottom (min max-bottom (1- (point))))
959 ))
960 )
961
962
963 (defun hide-ifdef-block ()
964 "Hide the ifdef block (true or false part) enclosing or before the cursor."
965 (interactive)
966 (if (not hide-ifdef-mode)
967 (hide-ifdef-mode 1))
968 (if buffer-read-only (toggle-read-only))
969 (setq selective-display t)
970 (let (top bottom)
971 (hif-find-ifdef-block) ; set top and bottom - dynamic scoping
972 (hide-ifdef-region top bottom)
973 (if hide-ifdef-lines
974 (progn
975 (hif-hide-line top)
976 (hif-hide-line (1+ bottom))))
977 (setq hide-ifdef-hiding t)
978 )
979 (if (or hide-ifdef-read-only hif-outside-read-only)
980 (toggle-read-only))
981 )
982
983
984 (defun show-ifdef-block ()
985 "Show the ifdef block (true or false part) enclosing or before the cursor."
986 (interactive)
987 (let ((old-read-only buffer-read-only))
988 (if old-read-only (toggle-read-only))
989 (if hide-ifdef-lines
990 (save-excursion
991 (beginning-of-line)
992 (hif-show-ifdef-region (1- (point)) (progn (end-of-line) (point))))
993
994 (let (top bottom)
995 (hif-find-ifdef-block)
996 (hif-show-ifdef-region (1- top) bottom))
997 )
998
999 ; restore read only status since we dont know if all is shown.
1000 (if old-read-only (toggle-read-only))
1001 ))
1002
1003
1004
1005 ;;; defininition alist support
1006
1007 (defvar hide-ifdef-define-alist nil
1008 "A global assoc list of pre-defined symbol lists")
1009
1010 (defun hif-compress-define-list (env)
1011 "Compress the define list ENV into a list of defined symbols only."
1012 (let ((defs (mapcar '(lambda (arg)
1013 (if (hif-lookup (car arg)) (car arg)))
1014 env))
1015 (new-defs nil))
1016 (while defs
1017 (if (car defs)
1018 (setq new-defs (cons (car defs) new-defs)))
1019 (setq defs (cdr defs)))
1020 new-defs
1021 ))
1022
1023 (defun hide-ifdef-set-define-alist (name)
1024 "Set the association for NAME to hide-ifdef-env."
1025 (interactive "SSet define list: ")
1026 (setq hide-ifdef-define-alist
1027 (cons (cons name (hif-compress-define-list hide-ifdef-env))
1028 hide-ifdef-define-alist))
1029 )
1030
1031 (defun hide-ifdef-use-define-alist (name)
1032 "Set hide-ifdef-env to the define list specified by NAME."
1033 (interactive "SUse define list: ")
1034 (let ((define-list (assoc name hide-ifdef-define-alist)))
1035 (if define-list
1036 (setq hide-ifdef-env
1037 (mapcar '(lambda (arg) (cons arg t))
1038 (cdr define-list)))
1039 (error "No define list for %s" name))
1040 (if hide-ifdef-hiding (hide-ifdefs))
1041 )
1042 )
1043
1044 ;===%%SF%% exports (End) ===