1 ;;; hideif.el --- hides selected code within ifdef
3 ;; Copyright (C) 1988, 1994, 2001-2014 Free Software Foundation, Inc.
5 ;; Author: Brian Marick
6 ;; Daniel LaLiberte <liberte@holonexus.org>
7 ;; Maintainer: emacs-devel@gnu.org
8 ;; Keywords: c, outlines
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
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. Support complete C/C++ expression and precedence.
39 ;; It will automatically scan for new #define symbols and macros on the way
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
46 ;; You can make your buffer read-only while hide-ifdef-hiding by setting
47 ;; hide-ifdef-read-only to a non-nil value. You can toggle this
48 ;; variable with hide-ifdef-toggle-read-only (C-c @ C-q).
50 ;; You can undo the effect of hide-ifdefs by typing
52 ;; M-x show-ifdefs or C-c @ s
54 ;; Use M-x hide-ifdef-define (C-c @ d) to define a symbol.
55 ;; Use M-x hide-ifdef-undef (C-c @ u) to undefine a symbol.
57 ;; If you define or undefine a symbol while hide-ifdef-mode is in effect,
58 ;; the display will be updated. Only the define list for the current
59 ;; buffer will be affected. You can save changes to the local define
60 ;; list with hide-ifdef-set-define-alist. This adds entries
61 ;; to hide-ifdef-define-alist.
63 ;; If you have defined a hide-ifdef-mode-hook, you can set
64 ;; up a list of symbols that may be used by hide-ifdefs as in the
67 ;; (add-hook 'hide-ifdef-mode-hook
69 ;; (unless hide-ifdef-define-alist
70 ;; (setq hide-ifdef-define-alist
72 ;; (list2 TWO THREE))))
73 ;; (hide-ifdef-use-define-alist 'list2))) ; use list2 by default
75 ;; You can call hide-ifdef-use-define-alist (C-c @ U) at any time to specify
76 ;; another list to use.
78 ;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
79 ;; set hide-ifdef-initially to non-nil.
81 ;; If you set hide-ifdef-lines to t, hide-ifdefs hides all the #ifdef lines.
82 ;; In the absence of highlighting, that might be a bad idea. If you set
83 ;; hide-ifdef-lines to nil (the default), the surrounding preprocessor
84 ;; lines will be displayed. That can be confusing in its own
85 ;; right. Other variations on display are possible, but not much
88 ;; You can explicitly hide or show individual ifdef blocks irrespective
89 ;; of the define list by using hide-ifdef-block and show-ifdef-block.
91 ;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
92 ;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
94 ;; If you have minor-mode-alist in your mode line (the default) two labels
95 ;; may appear. "Ifdef" will appear when hide-ifdef-mode is active. "Hiding"
96 ;; will appear when text may be hidden ("hide-ifdef-hiding" is non-nil).
98 ;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
99 ;; Extensively modified by Daniel LaLiberte (while at Gould).
101 ;; Extensively modified by Luke Lee in 2013 to support complete C expression
102 ;; evaluation and argumented macro expansion.
109 (defgroup hide-ifdef nil
110 "Hide selected code within `ifdef'."
113 (defcustom hide-ifdef-initially nil
114 "Non-nil means call `hide-ifdefs' when Hide-Ifdef mode is first activated."
118 (defcustom hide-ifdef-read-only nil
119 "Set to non-nil if you want buffer to be read-only while hiding text."
123 (defcustom hide-ifdef-lines nil
124 "Non-nil means hide the #ifX, #else, and #endif lines."
128 (defcustom hide-ifdef-shadow nil
129 "Non-nil means shadow text instead of hiding it."
134 (defface hide-ifdef-shadow
'((t (:inherit shadow
)))
135 "Face for shadowing ifdef blocks."
139 (defcustom hide-ifdef-exclude-define-regexp nil
140 "Ignore #define names if those names match this exclusion pattern."
143 (defvar hide-ifdef-mode-submap
144 ;; Set up the submap that goes after the prefix key.
145 (let ((map (make-sparse-keymap)))
146 (define-key map
"d" 'hide-ifdef-define
)
147 (define-key map
"u" 'hide-ifdef-undef
)
148 (define-key map
"D" 'hide-ifdef-set-define-alist
)
149 (define-key map
"U" 'hide-ifdef-use-define-alist
)
151 (define-key map
"h" 'hide-ifdefs
)
152 (define-key map
"s" 'show-ifdefs
)
153 (define-key map
"\C-d" 'hide-ifdef-block
)
154 (define-key map
"\C-s" 'show-ifdef-block
)
156 (define-key map
"\C-q" 'hide-ifdef-toggle-read-only
)
157 (define-key map
"\C-w" 'hide-ifdef-toggle-shadowing
)
158 (substitute-key-definition
159 'toggle-read-only
'hide-ifdef-toggle-outside-read-only map
)
161 "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'.")
163 (defconst hide-ifdef-mode-prefix-key
"\C-c@"
164 "Prefix key for all Hide-Ifdef mode commands.")
166 (defvar hide-ifdef-mode-map
167 ;; Set up the mode's main map, which leads via the prefix key to the submap.
168 (let ((map (make-sparse-keymap)))
169 (define-key map hide-ifdef-mode-prefix-key hide-ifdef-mode-submap
)
171 "Keymap used with `hide-ifdef-mode'.")
173 (easy-menu-define hide-ifdef-mode-menu hide-ifdef-mode-map
174 "Menu for `hide-ifdef-mode'."
176 ["Hide some ifdefs" hide-ifdefs
177 :help
"Hide the contents of some #ifdefs"]
178 ["Show all ifdefs" show-ifdefs
179 :help
"Cancel the effects of `hide-ifdef': show the contents of all #ifdefs"]
180 ["Hide ifdef block" hide-ifdef-block
181 :help
"Hide the ifdef block (true or false part) enclosing or before the cursor"]
182 ["Show ifdef block" show-ifdef-block
183 :help
"Show the ifdef block (true or false part) enclosing or before the cursor"]
184 ["Define a variable..." hide-ifdef-define
185 :help
"Define a VAR so that #ifdef VAR would be included"]
186 ["Undefine a variable..." hide-ifdef-undef
187 :help
"Undefine a VAR so that #ifdef VAR would not be included"]
188 ["Define an alist..." hide-ifdef-set-define-alist
189 :help
"Set the association for NAME to `hide-ifdef-env'"]
190 ["Use an alist..." hide-ifdef-use-define-alist
191 :help
"Set `hide-ifdef-env' to the define list specified by NAME"]
192 ["Toggle read only" hide-ifdef-toggle-read-only
193 :style toggle
:selected hide-ifdef-read-only
194 :help
"Buffer should be read-only while hiding text"]
195 ["Toggle shadowing" hide-ifdef-toggle-shadowing
196 :style toggle
:selected hide-ifdef-shadow
197 :help
"Text should be shadowed instead of hidden"]))
199 (defvar hide-ifdef-hiding nil
200 "Non-nil when text may be hidden.")
202 (or (assq 'hide-ifdef-hiding minor-mode-alist
)
203 (setq minor-mode-alist
204 (cons '(hide-ifdef-hiding " Hiding")
207 ;; Fix c-mode syntax table so we can recognize whole symbols.
208 (defvar hide-ifdef-syntax-table
209 (let ((st (copy-syntax-table c-mode-syntax-table
)))
210 (modify-syntax-entry ?_
"w" st
)
211 (modify-syntax-entry ?
& "." st
)
212 (modify-syntax-entry ?\|
"." st
)
214 "Syntax table used for tokenizing #if expressions.")
216 (defvar hide-ifdef-env nil
217 "An alist of defined symbols and their values.")
219 (defvar hif-outside-read-only nil
220 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
223 (define-minor-mode hide-ifdef-mode
224 "Toggle features to hide/show #ifdef blocks (Hide-Ifdef mode).
225 With a prefix argument ARG, enable Hide-Ifdef mode if ARG is
226 positive, and disable it otherwise. If called from Lisp, enable
227 the mode if ARG is omitted or nil.
229 Hide-Ifdef mode is a buffer-local minor mode for use with C and
230 C-like major modes. When enabled, code within #ifdef constructs
231 that the C preprocessor would eliminate may be hidden from view.
232 Several variables affect how the hiding is done:
235 An association list of defined and undefined symbols for the
236 current buffer. Initially, the global value of `hide-ifdef-env'
239 `hide-ifdef-define-alist'
240 An association list of defined symbol lists.
241 Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
242 and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
243 from one of the lists in `hide-ifdef-define-alist'.
246 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
247 #endif lines when hiding.
249 `hide-ifdef-initially'
250 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
253 `hide-ifdef-read-only'
254 Set to non-nil if you want to make buffers read only while hiding.
255 After `show-ifdefs', read-only status is restored to previous value.
257 \\{hide-ifdef-mode-map}"
258 :group
'hide-ifdef
:lighter
" Ifdef"
261 ;; inherit global values
262 (set (make-local-variable 'hide-ifdef-env
)
263 (default-value 'hide-ifdef-env
))
264 (set (make-local-variable 'hide-ifdef-hiding
)
265 (default-value 'hide-ifdef-hiding
))
266 (set (make-local-variable 'hif-outside-read-only
) buffer-read-only
)
267 (set (make-local-variable 'line-move-ignore-invisible
) t
)
268 (add-hook 'change-major-mode-hook
269 (lambda () (hide-ifdef-mode -
1)) nil t
)
271 (add-to-invisibility-spec '(hide-ifdef . t
))
273 (if hide-ifdef-initially
276 ;; else end hide-ifdef-mode
277 (kill-local-variable 'line-move-ignore-invisible
)
278 (remove-from-invisibility-spec '(hide-ifdef . t
))
279 (when hide-ifdef-hiding
283 (defun hif-show-all ()
284 "Show all of the text in the current buffer."
286 (hif-show-ifdef-region (point-min) (point-max)))
288 ;; By putting this on after-revert-hook, we arrange that it only
289 ;; does anything when revert-buffer avoids turning off the mode.
290 ;; (That can happen in VC.)
291 (defun hif-after-revert-function ()
292 (and hide-ifdef-mode hide-ifdef-hiding
294 (add-hook 'after-revert-hook
'hif-after-revert-function
)
296 (defun hif-end-of-line ()
298 (while (= (logand 1 (skip-chars-backward "\\\\")) 1)
301 (defun hide-ifdef-region-internal (start end
)
302 (remove-overlays start end
'hide-ifdef t
)
303 (let ((o (make-overlay start end
)))
304 (overlay-put o
'hide-ifdef t
)
305 (if hide-ifdef-shadow
306 (overlay-put o
'face
'hide-ifdef-shadow
)
307 (overlay-put o
'invisible
'hide-ifdef
))))
309 (defun hide-ifdef-region (start end
)
310 "START is the start of a #if or #else form. END is the ending part.
311 Everything including these lines is made invisible."
313 (goto-char start
) (hif-end-of-line) (setq start
(point))
314 (goto-char end
) (hif-end-of-line) (setq end
(point))
315 (hide-ifdef-region-internal start end
)))
317 (defun hif-show-ifdef-region (start end
)
318 "Everything between START and END is made visible."
319 (remove-overlays start end
'hide-ifdef t
))
322 ;;===%%SF%% evaluation (Start) ===
324 ;; It is not useful to set this to anything but `eval'.
325 ;; In fact, the variable might as well be eliminated.
326 (defvar hide-ifdef-evaluator
'eval
327 "The function to use to evaluate a form.
328 The evaluator is given a canonical form and returns t if text under
329 that form should be displayed.")
331 (defvar hif-undefined-symbol nil
332 "...is by default considered to be false.")
335 (defun hif-set-var (var value
)
336 "Prepend (VAR VALUE) pair to `hide-ifdef-env'."
337 (setq hide-ifdef-env
(cons (cons var value
) hide-ifdef-env
)))
339 (declare-function semantic-c-hideif-lookup
"semantic/bovine/c" (var))
340 (declare-function semantic-c-hideif-defined
"semantic/bovine/c" (var))
342 (defun hif-lookup (var)
343 (or (when (bound-and-true-p semantic-c-takeover-hideif
)
344 (semantic-c-hideif-lookup var
))
345 (let ((val (assoc var hide-ifdef-env
)))
348 hif-undefined-symbol
))))
350 (defun hif-defined (var)
352 ((bound-and-true-p semantic-c-takeover-hideif
)
353 (semantic-c-hideif-defined var
))
354 ((assoc var hide-ifdef-env
) 1)
357 ;;===%%SF%% evaluation (End) ===
361 ;;===%%SF%% parsing (Start) ===
362 ;;; The code that understands what ifs and ifdef in files look like.
364 (defconst hif-cpp-prefix
"\\(^\\|\r\\)[ \t]*#[ \t]*")
365 (defconst hif-ifxdef-regexp
(concat hif-cpp-prefix
"if\\(n\\)?def"))
366 (defconst hif-ifndef-regexp
(concat hif-cpp-prefix
"ifndef"))
367 (defconst hif-ifx-regexp
(concat hif-cpp-prefix
"if\\(n?def\\)?[ \t]+"))
368 (defconst hif-elif-regexp
(concat hif-cpp-prefix
"elif"))
369 (defconst hif-else-regexp
(concat hif-cpp-prefix
"else"))
370 (defconst hif-endif-regexp
(concat hif-cpp-prefix
"endif"))
371 (defconst hif-ifx-else-endif-regexp
372 (concat hif-ifx-regexp
"\\|" hif-elif-regexp
"\\|" hif-else-regexp
"\\|"
374 (defconst hif-macro-expr-prefix-regexp
375 (concat hif-cpp-prefix
"\\(if\\(n?def\\)?\\|elif\\|define\\)[ \t]+"))
377 (defconst hif-white-regexp
"[ \t]*")
378 (defconst hif-define-regexp
379 (concat hif-cpp-prefix
"\\(define\\|undef\\)"))
380 (defconst hif-id-regexp
381 (concat "[[:alpha:]_][[:alnum:]_]*"))
382 (defconst hif-macroref-regexp
383 (concat hif-white-regexp
"\\(" hif-id-regexp
"\\)" hif-white-regexp
386 "\\(" hif-id-regexp
"\\)?" hif-white-regexp
387 "\\(" "," hif-white-regexp hif-id-regexp hif-white-regexp
"\\)*"
388 "\\(\\.\\.\\.\\)?" hif-white-regexp
392 ;; Store the current token and the whole token list during parsing.
393 ;; Bound dynamically.
395 (defvar hif-token-list
)
397 (defconst hif-token-alist
403 ("<<" . hif-shiftleft
)
404 (">>" . hif-shiftright
)
406 ;; Note: we include tokens like `=' which aren't supported by CPP's
407 ;; expression syntax, because they are still relevant for the tokenizer,
408 ;; especially in conjunction with ##.
410 ("!=" . hif-notequal
)
411 ("##" . hif-token-concat
)
418 (">=" . hif-greater-equal
)
419 ("<=" . hif-less-equal
)
425 ("?" . hif-conditional
)
428 ("#" . hif-stringify
)
431 (defconst hif-valid-token-list
(mapcar 'cdr hif-token-alist
))
433 (defconst hif-token-regexp
434 (concat (regexp-opt (mapcar 'car hif-token-alist
))
435 "\\|0x[0-9a-fA-F]+\\.?[0-9a-fA-F]*"
436 "\\|[0-9]+\\.?[0-9]*" ;; decimal/octal
439 (defconst hif-string-literal-regexp
"\\(\"\\(?:[^\"\\]\\|\\\\.\\)*\"\\)")
441 (defun hif-string-to-number (string &optional base
)
442 "Like `string-to-number', but it understands non-decimal floats."
443 (if (or (not base
) (= base
10))
444 (string-to-number string base
)
445 (let* ((parts (split-string string
"\\." t
"[ \t]+"))
447 (fraclen (length frac
))
448 (quot (expt (if (zerop fraclen
)
450 (* base
1.0)) fraclen
)))
451 (/ (string-to-number (concat (car parts
) frac
) base
) quot
))))
453 ;; The dynamic binding variable `hif-simple-token-only' is shared only by
454 ;; `hif-tokenize' and `hif-find-define'. The purpose is to prevent `hif-tokenize'
455 ;; from returning one more value to indicate a simple token is scanned. This help
456 ;; speeding up macro evaluation on those very simple cases like integers or
458 ;; Check the long comments before `hif-find-define' for more details. [lukelee]
459 (defvar hif-simple-token-only
)
461 (defun hif-tokenize (start end
)
462 "Separate string between START and END into a list of tokens."
463 (let ((token-list nil
))
464 (setq hif-simple-token-only t
)
465 (with-syntax-table hide-ifdef-syntax-table
468 (while (progn (forward-comment (point-max)) (< (point) end
))
469 ;; (message "expr-start = %d" expr-start) (sit-for 1)
471 ((looking-at "\\\\\n")
474 ((looking-at hif-string-literal-regexp
)
475 (push (substring-no-properties (match-string 1)) token-list
)
476 (goto-char (match-end 0)))
478 ((looking-at hif-token-regexp
)
479 (let ((token (buffer-substring-no-properties
480 (point) (match-end 0))))
481 (goto-char (match-end 0))
482 ;; (message "token: %s" token) (sit-for 1)
484 (or (cdr (assoc token hif-token-alist
))
485 (if (string-equal token
"defined") 'hif-defined
)
487 ;; 1. postfix 'l', 'll', 'ul' and 'ull'
488 ;; 2. floating number formats (like 1.23e4)
489 ;; 3. 098 is interpreted as octal conversion error
490 (if (string-match "0x\\([0-9a-fA-F]+\\.?[0-9a-fA-F]*\\)"
492 (hif-string-to-number (match-string 1 token
) 16)) ;; hex
493 (if (string-match "\\`0[0-9]+\\(\\.[0-9]+\\)?\\'" token
)
494 (hif-string-to-number token
8)) ;; octal
495 (if (string-match "\\`[1-9][0-9]*\\(\\.[0-9]+\\)?\\'"
497 (string-to-number token
)) ;; decimal
498 (prog1 (intern token
)
499 (setq hif-simple-token-only nil
)))
502 (t (error "Bad #if expression: %s" (buffer-string)))))))
504 (nreverse token-list
)))
506 ;;------------------------------------------------------------------------
507 ;; Translate C preprocessor #if expressions using recursive descent.
508 ;; This parser was limited to the operators &&, ||, !, and "defined".
509 ;; Added ==, !=, +, and -. Gary Oberbrunner, garyo@avs.com, 8/9/94
511 ;; Implement the C language operator precedence table. Add all those
512 ;; missing operators that could be used in macros. Luke Lee 2013-09-04
514 ;; | Operator Type | Operator | Associativity |
515 ;; +----------------------+-----------------------------+---------------+
516 ;; | Primary Expression | () [] . -> expr++ expr-- | left-to-right |
517 ;; | Unary Operators | * & + - ! ~ ++expr --expr | right-to-left |
518 ;; | | (typecast) sizeof | |
519 ;; | Binary Operators | * / % | left-to-right |
529 ;; | Ternary Operator | ?: | right-to-left |
530 ;; x| Assignment Operators | = += -= *= /= %= >>= <<= &= | right-to-left |
532 ;; | Comma | , | left-to-right |
534 (defsubst hif-nexttoken
()
535 "Pop the next token from token-list into the let variable `hif-token'."
536 (setq hif-token
(pop hif-token-list
)))
538 (defsubst hif-if-valid-identifier-p
(id)
539 (not (or (numberp id
)
542 (defun hif-define-operator (tokens)
543 "`Upgrade' hif-define xxx to '(hif-define xxx)' so it won't be subsitituted."
546 (while (setq tok
(pop tokens
))
548 (if (eq tok
'hif-defined
)
550 (setq tok
(cadr tokens
))
551 (if (eq (car tokens
) 'hif-lparen
)
552 (if (and (hif-if-valid-identifier-p tok
)
553 (eq (caddr tokens
) 'hif-rparen
))
554 (setq tokens
(cdddr tokens
))
555 (error "#define followed by non-identifier: %S" tok
))
556 (setq tok
(car tokens
)
558 (unless (hif-if-valid-identifier-p tok
)
559 (error "#define followed by non-identifier: %S" tok
)))
560 (list 'hif-defined
'hif-lparen tok
'hif-rparen
))
565 (defun hif-flatten (l)
568 (mapcar (lambda (x) (if (listp x
)
572 (defun hif-expand-token-list (tokens &optional macroname expand_list
)
573 "Perform expansion on TOKENS till everything expanded.
574 Self-reference (directly or indirectly) tokens are not expanded.
575 EXPAND_LIST is the list of macro names currently being expanded, use for
576 detecting self-reference."
577 (catch 'self-referencing
579 (remains (hif-define-operator
580 (hif-token-concatenation
581 (hif-token-stringification tokens
))))
584 (setq expand_list
(cons macroname expand_list
)))
585 ;; Expanding all tokens till list exhausted
586 (while (setq tok
(pop remains
))
587 (if (memq tok expand_list
)
588 ;; For self-referencing tokens, don't expand it
589 (throw 'self-referencing tokens
))
592 ((or (memq tok hif-valid-token-list
)
597 ((setq rep
(hif-lookup tok
))
599 (eq (car rep
) 'hif-define-macro
)) ; A defined macro
600 ;; Recursively expand it
601 (if (cadr rep
) ; Argument list is not nil
602 (if (not (eq (car remains
) 'hif-lparen
))
603 ;; No argument, no invocation
605 ;; Argumented macro, get arguments and invoke it.
606 ;; Dynamically bind hif-token-list and hif-token
607 ;; for hif-macro-supply-arguments
608 (let* ((hif-token-list (cdr remains
))
610 (parmlist (mapcar 'hif-expand-token-list
611 (hif-get-argument-list
614 (hif-expand-token-list
615 (hif-macro-supply-arguments tok parmlist
)
617 (setq remains
(cons hif-token hif-token-list
))
619 ;; Argument list is nil, direct expansion
620 (setq rep
(hif-expand-token-list
621 (caddr rep
) ; Macro's token list
623 ;; Replace all remaining references immediately
624 (setq remains
(substitute tok rep remains
))
626 ;; Lookup tok returns an atom
629 ;;[2013-10-22 16:06:12 +0800] Must keep the token, removing
630 ;; this token might results in an incomplete expression that
631 ;; cannot be parsed further.
632 ;;((= 1 (hif-defined tok)) ; defined (hif-defined tok)=1,
633 ;; ;;but empty (hif-lookup tok)=nil, thus remove this token
634 ;; (setq remains (delete tok remains))
642 (hif-flatten (nreverse expanded
)))))
644 (defun hif-parse-exp (token-list &optional macroname
)
645 "Parse the TOKEN-LIST.
646 Return translated list in prefix form. MACRONAME is applied when invoking
647 macros to prevent self-reference."
648 (let ((hif-token-list (hif-expand-token-list token-list macroname
)))
653 (if hif-token
; is there still a token?
654 (error "Error: unexpected token: %s" hif-token
)))))
656 (defun hif-exprlist ()
657 "Parse an exprlist: expr { ',' expr}."
658 (let ((result (hif-expr)))
659 (if (eq hif-token
'hif-comma
)
660 (let ((temp (list result
)))
664 (push (hif-expr) temp
)
665 (eq hif-token
'hif-comma
)))
666 (cons 'hif-comma
(nreverse temp
)))
670 "Parse an expression as found in #if.
671 expr : or-expr | or-expr '?' expr ':' expr."
672 (let ((result (hif-or-expr))
674 (while (eq hif-token
'hif-conditional
)
676 (setq middle
(hif-expr))
677 (if (eq hif-token
'hif-colon
)
680 (setq result
(list 'hif-conditional result middle
(hif-expr))))
681 (error "Error: unexpected token: %s" hif-token
)))
684 (defun hif-or-expr ()
685 "Parse an or-expr : and-expr | or-expr '||' and-expr."
686 (let ((result (hif-and-expr)))
687 (while (eq hif-token
'hif-or
)
689 (setq result
(list 'hif-or result
(hif-and-expr))))
692 (defun hif-and-expr ()
693 "Parse an and-expr : logior-expr | and-expr '&&' logior-expr."
694 (let ((result (hif-logior-expr)))
695 (while (eq hif-token
'hif-and
)
697 (setq result
(list 'hif-and result
(hif-logior-expr))))
700 (defun hif-logior-expr ()
701 "Parse a logor-expr : logxor-expr | logor-expr '|' logxor-expr."
702 (let ((result (hif-logxor-expr)))
703 (while (eq hif-token
'hif-logior
)
705 (setq result
(list 'hif-logior result
(hif-logxor-expr))))
708 (defun hif-logxor-expr ()
709 "Parse a logxor-expr : logand-expr | logxor-expr '^' logand-expr."
710 (let ((result (hif-logand-expr)))
711 (while (eq hif-token
'hif-logxor
)
713 (setq result
(list 'hif-logxor result
(hif-logand-expr))))
716 (defun hif-logand-expr ()
717 "Parse a logand-expr : eq-expr | logand-expr '&' eq-expr."
718 (let ((result (hif-eq-expr)))
719 (while (eq hif-token
'hif-logand
)
721 (setq result
(list 'hif-logand result
(hif-eq-expr))))
724 (defun hif-eq-expr ()
725 "Parse an eq-expr : comp | eq-expr `=='|`!=' comp."
726 (let ((result (hif-comp-expr))
728 (while (memq hif-token
'(hif-equal hif-notequal
))
729 (setq eq-token hif-token
)
731 (setq result
(list eq-token result
(hif-comp-expr))))
734 (defun hif-comp-expr ()
735 "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
736 (let ((result (hif-logshift-expr))
738 (while (memq hif-token
'(hif-greater hif-less hif-greater-equal
740 (setq comp-token hif-token
)
742 (setq result
(list comp-token result
(hif-logshift-expr))))
745 (defun hif-logshift-expr ()
746 "Parse a logshift : math | logshift `<<'|`>>' math."
747 (let ((result (hif-math))
749 (while (memq hif-token
'(hif-shiftleft hif-shiftright
))
750 (setq shift-token hif-token
)
752 (setq result
(list shift-token result
(hif-math))))
756 "Parse an expression with + or -.
757 math : muldiv | math '+|-' muldiv."
758 (let ((result (hif-muldiv-expr))
760 (while (memq hif-token
'(hif-plus hif-minus
))
761 (setq math-op hif-token
)
763 (setq result
(list math-op result
(hif-muldiv-expr))))
766 (defun hif-muldiv-expr ()
767 "Parse an expression with *,/,%.
768 muldiv : factor | muldiv '*|/|%' factor."
769 (let ((result (hif-factor))
771 (while (memq hif-token
'(hif-multiply hif-divide hif-modulo
))
772 (setq math-op hif-token
)
774 (setq result
(list math-op result
(hif-factor))))
779 factor : '!' factor | '~' factor | '(' expr ')' | 'defined(' id ')' |
780 'id(parmlist)' | strings | id."
782 ((eq hif-token
'hif-not
)
784 (list 'hif-not
(hif-factor)))
786 ((eq hif-token
'hif-lognot
)
788 (list 'hif-lognot
(hif-factor)))
790 ((eq hif-token
'hif-lparen
)
792 (let ((result (hif-exprlist)))
793 (if (not (eq hif-token
'hif-rparen
))
794 (error "Bad token in parenthesized expression: %s" hif-token
)
798 ((eq hif-token
'hif-defined
)
800 (let ((paren (when (eq hif-token
'hif-lparen
) (hif-nexttoken) t
))
802 (if (memq hif-token
'(or and not hif-defined hif-lparen hif-rparen
))
803 (error "Error: unexpected token: %s" hif-token
))
806 (unless (eq hif-token
'hif-rparen
)
807 (error "Error: expected \")\" after identifier")))
809 `(hif-defined (quote ,ident
))))
812 (prog1 hif-token
(hif-nexttoken)))
814 (hif-string-concatenation))
817 ((memq hif-token
'(hif-minus hif-plus
))
818 (list (prog1 hif-token
(hif-nexttoken)) 0 (hif-factor)))
821 (let ((ident hif-token
))
823 (if (eq hif-token
'hif-lparen
)
824 (hif-place-macro-invocation ident
)
825 `(hif-lookup (quote ,ident
)))))))
827 (defun hif-get-argument-list (ident)
829 (parmlist nil
) ; A "token" list of parameters, will later be parsed
832 (while (or (not (eq (hif-nexttoken) 'hif-rparen
))
834 (if (eq (car (last parm
)) 'hif-comma
)
837 ((eq hif-token
'hif-lparen
)
838 (setq nest
(1+ nest
)))
839 ((eq hif-token
'hif-rparen
)
840 (setq nest
(1- nest
)))
841 ((and (eq hif-token
'hif-comma
)
843 (push (nreverse parm
) parmlist
)
845 (push hif-token parm
))
847 (push (nreverse parm
) parmlist
) ; Okay even if PARM is nil
848 (hif-nexttoken) ; Drop the `hif-rparen', get next token
849 (nreverse parmlist
)))
851 (defun hif-place-macro-invocation (ident)
852 (let ((parmlist (hif-get-argument-list ident
)))
853 `(hif-invoke (quote ,ident
) (quote ,parmlist
))))
855 (defun hif-string-concatenation ()
856 "Parse concatenated strings: string | strings string."
857 (let ((result (substring-no-properties hif-token
)))
858 (while (stringp (hif-nexttoken))
860 (substring result
0 -
1) ; remove trailing '"'
861 (substring hif-token
1)))) ; remove leading '"'
864 (defun hif-define-macro (parmlist token-body
)
865 "A marker for defined macro with arguments.
866 This macro cannot be evaluated alone without parameters inputed."
867 ;;TODO: input arguments at run time, use minibuffer to query all arguments
869 "Argumented macro cannot be evaluated without passing any parameter"))
871 (defun hif-stringify (a)
872 "Stringify a number, string or symbol."
875 (number-to-string a
))
879 (concat "\"" a
"\""))
881 (error "Invalid token to stringify"))))
883 (defun intern-safe (str)
887 (defun hif-token-concat (a b
)
888 "Concatenate two tokens into a longer token.
889 Currently support only simple token concatenation. Also support weird (but
890 valid) token concatenation like '>' ## '>' becomes '>>'. Here we take care only
891 those that can be evaluated during preprocessing time and ignore all those that
892 can only be evaluated at C(++) runtime (like '++', '--' and '+='...)."
893 (if (or (memq a hif-valid-token-list
)
894 (memq b hif-valid-token-list
))
895 (let* ((ra (car (rassq a hif-token-alist
)))
896 (rb (car (rassq b hif-token-alist
)))
898 (cdr (assoc (concat ra rb
) hif-token-alist
)))))
900 ;;(error "Invalid token to concatenate")
901 (error "Concatenating \"%s\" and \"%s\" does not give a valid \
903 (or ra
(symbol-name a
))
904 (or rb
(symbol-name b
)))))
905 (intern-safe (concat (hif-stringify a
)
906 (hif-stringify b
)))))
908 (defun hif-mathify (val)
909 "Treat VAL as a number: if it's t or nil, use 1 or 0."
914 (defun hif-conditional (a b c
)
915 (if (not (zerop (hif-mathify a
))) (hif-mathify b
) (hif-mathify c
)))
917 (and (not (zerop (hif-mathify a
))) (not (zerop (hif-mathify b
)))))
919 (or (not (zerop (hif-mathify a
))) (not (zerop (hif-mathify b
)))))
921 (zerop (hif-mathify a
)))
922 (defun hif-lognot (a)
923 (lognot (hif-mathify a
)))
925 (defmacro hif-mathify-binop
(fun)
927 ,(format "Like `%s' but treat t and nil as 1 and 0." fun
)
928 (,fun
(hif-mathify a
) (hif-mathify b
))))
930 (defun hif-shiftleft (a b
)
931 (setq a
(hif-mathify a
))
932 (setq b
(hif-mathify b
))
937 (defun hif-shiftright (a b
)
938 (setq a
(hif-mathify a
))
939 (setq b
(hif-mathify b
))
945 (defalias 'hif-multiply
(hif-mathify-binop *))
946 (defalias 'hif-divide
(hif-mathify-binop /))
947 (defalias 'hif-modulo
(hif-mathify-binop %
))
948 (defalias 'hif-plus
(hif-mathify-binop +))
949 (defalias 'hif-minus
(hif-mathify-binop -
))
950 (defalias 'hif-equal
(hif-mathify-binop =))
951 (defalias 'hif-notequal
(hif-mathify-binop /=))
952 (defalias 'hif-greater
(hif-mathify-binop >))
953 (defalias 'hif-less
(hif-mathify-binop <))
954 (defalias 'hif-greater-equal
(hif-mathify-binop >=))
955 (defalias 'hif-less-equal
(hif-mathify-binop <=))
956 (defalias 'hif-logior
(hif-mathify-binop logior
))
957 (defalias 'hif-logxor
(hif-mathify-binop logxor
))
958 (defalias 'hif-logand
(hif-mathify-binop logand
))
961 (defun hif-comma (&rest expr
)
962 "Evaluate a list of EXPR, return the result of the last item."
966 (setq result
(funcall hide-ifdef-evaluator e
))))
969 (defun hif-token-stringification (l)
970 "Scan token list for `hif-stringify' ('#') token and stringify the next token."
973 (push (if (eq (car l
) 'hif-stringify
)
976 (hif-stringify (cadr l
))
977 (error "No token to stringify"))
984 (defun hif-token-concatenation (l)
985 "Scan token list for `hif-token-concat' ('##') token and concatenate two tokens."
989 (while (eq (car l
) 'hif-token-concat
)
991 (error "No token before ## to concatenate"))
993 (error "No token after ## to concatenate"))
994 (setq prev
(hif-token-concat prev
(cadr l
)))
997 (setq result
(append result
(list prev
))))
1001 (append result
(list prev
))
1004 (defun hif-delimit (lis atom
)
1005 (nconc (mapcan (lambda (l) (list l atom
))
1009 ;; Perform token replacement:
1010 (defun hif-macro-supply-arguments (macro-name actual-parms
)
1011 "Expand a macro call, replace ACTUAL-PARMS in the macro body."
1012 (let* ((SA (assoc macro-name hide-ifdef-env
))
1015 (eq (cadr SA
) 'hif-define-macro
)
1017 (formal-parms (and macro
(car macro
)))
1018 (macro-body (and macro
(cadr macro
)))
1025 (when (and actual-parms formal-parms macro-body
)
1026 ;; For each actual parameter, evaluate each one and associate it
1027 ;; with an actual parameter, put it into local table and finally
1028 ;; evaluate the macro body.
1029 (if (setq etc
(eq (car formal-parms
) 'hif-etc
))
1030 ;; Take care of `hif-etc' first. Prefix `hif-comma' back if needed.
1031 (setq formal-parms
(cdr formal-parms
)))
1032 (setq formal-count
(length formal-parms
)
1033 actual-count
(length actual-parms
))
1035 (if (> formal-count actual-count
)
1036 (error "Too few parmameter for macro %S" macro-name
)
1037 (if (< formal-count actual-count
)
1039 (error "Too many parameters for macro %S" macro-name
))))
1041 ;; Perform token replacement on the MACRO-BODY with the parameters
1042 (while (setq formal
(pop formal-parms
))
1043 ;; Prevent repetitive substitutation, thus cannot use `subst'
1045 ;; #define mac(a,b) (a+b)
1046 ;; #define testmac mac(b,y)
1047 ;; testmac should expand to (b+y): replace of argument a and b
1048 ;; occurs simultaneously, not sequentially. If sequentially,
1049 ;; according to the argument order, it will become:
1050 ;; 1. formal parm #1 'a' replaced by actual parm 'b', thus (a+b)
1052 ;; 2. formal parm #2 'b' replaced by actual parm 'y', thus (b+b)
1055 ;; Unlike `subst', `substitute' replace only the top level
1056 ;; instead of the whole tree; more importantly, it's not
1058 (substitute (if (and etc
(null formal-parms
))
1059 (hif-delimit actual-parms
'hif-comma
)
1062 (setq actual-parms
(cdr actual-parms
)))
1064 ;; Replacement completed, flatten the whole token list
1065 (setq macro-body
(hif-flatten macro-body
))
1067 ;; Stringification and token concatenation happens here
1068 (hif-token-concatenation (hif-token-stringification macro-body
)))))
1070 (defun hif-invoke (macro-name actual-parms
)
1071 "Invoke a macro by expanding it, reparse macro-body and finally invoke it."
1072 ;; Reparse the macro body and evaluate it
1073 (funcall hide-ifdef-evaluator
1075 (hif-macro-supply-arguments macro-name actual-parms
)
1078 ;;;----------- end of parser -----------------------
1081 (defun hif-canonicalize-tokens (regexp) ; For debugging
1082 "Return the expanded result of the scanned tokens."
1084 (re-search-forward regexp
)
1085 (let* ((curr-regexp (match-string 0))
1086 (defined (string-match hif-ifxdef-regexp curr-regexp
))
1087 (negate (and defined
1088 (string= (match-string 2 curr-regexp
) "n")))
1089 (hif-simple-token-only nil
) ;; Dynamic binding var for `hif-tokenize'
1090 (tokens (hif-tokenize (point)
1091 (progn (hif-end-of-line) (point)))))
1093 (setq tokens
(list 'hif-defined tokens
)))
1095 (setq tokens
(list 'hif-not tokens
)))
1098 (defun hif-canonicalize (regexp)
1099 "Return a Lisp expression for its condition by scanning current buffer.
1100 Do this when cursor is at the beginning of `regexp' (i.e. #ifX)."
1101 (let ((case-fold-search nil
))
1103 (re-search-forward regexp
)
1104 (let* ((curr-regexp (match-string 0))
1105 (defined (string-match hif-ifxdef-regexp curr-regexp
))
1106 (negate (and defined
1107 (string= (match-string 2 curr-regexp
) "n")))
1108 (hif-simple-token-only nil
) ; Dynamic binding for `hif-tokenize'
1109 (tokens (hif-tokenize (point)
1110 (progn (hif-end-of-line) (point)))))
1112 (setq tokens
(list 'hif-defined tokens
)))
1114 (setq tokens
(list 'hif-not tokens
)))
1115 (hif-parse-exp tokens
)))))
1117 (defun hif-find-any-ifX ()
1118 "Move to next #if..., or #ifndef, at point or after."
1119 ;; (message "find ifX at %d" (point))
1121 (re-search-forward hif-ifx-regexp
(point-max) t
)
1122 (beginning-of-line)))
1125 (defun hif-find-next-relevant ()
1126 "Move to next #if..., #elif..., #else, or #endif, after the current line."
1127 ;; (message "hif-find-next-relevant at %d" (point))
1129 ;; Avoid infinite recursion by only going to line-beginning if match found
1130 (if (re-search-forward hif-ifx-else-endif-regexp
(point-max) t
)
1131 (beginning-of-line)))
1133 (defun hif-find-previous-relevant ()
1134 "Move to previous #if..., #else, or #endif, before the current line."
1135 ;; (message "hif-find-previous-relevant at %d" (point))
1137 ;; Avoid infinite recursion by only going to line-beginning if match found
1138 (if (re-search-backward hif-ifx-else-endif-regexp
(point-min) t
)
1139 (beginning-of-line)))
1142 (defun hif-looking-at-ifX ()
1143 (looking-at hif-ifx-regexp
)) ; Should eventually see #if
1144 (defun hif-looking-at-endif ()
1145 (looking-at hif-endif-regexp
))
1146 (defun hif-looking-at-else ()
1147 (looking-at hif-else-regexp
))
1149 (defun hif-looking-at-elif ()
1150 (looking-at hif-elif-regexp
))
1153 (defun hif-ifdef-to-endif ()
1154 "If positioned at #ifX, #elif, or #else form, skip to corresponding #endif."
1155 ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
1156 (hif-find-next-relevant)
1157 (cond ((hif-looking-at-ifX)
1158 (hif-ifdef-to-endif) ; Find endif of nested if
1159 (hif-ifdef-to-endif)) ; Find outer endif or else
1160 ((hif-looking-at-elif)
1161 (hif-ifdef-to-endif))
1162 ((hif-looking-at-else)
1163 (hif-ifdef-to-endif)) ; Find endif following else
1164 ((hif-looking-at-endif)
1167 (error "Mismatched #ifdef #endif pair"))))
1170 (defun hif-endif-to-ifdef ()
1171 "If positioned at #endif form, skip backward to corresponding #ifX."
1172 ;; (message "hif-endif-to-ifdef at %d" (point))
1173 (let ((start (point)))
1174 (hif-find-previous-relevant)
1175 (if (= start
(point))
1176 (error "Mismatched #ifdef #endif pair")))
1177 (cond ((hif-looking-at-endif)
1178 (hif-endif-to-ifdef) ; find beginning of nested if
1179 (hif-endif-to-ifdef)) ; find beginning of outer if or else
1180 ((hif-looking-at-else)
1181 (hif-endif-to-ifdef))
1182 ((hif-looking-at-ifX)
1184 (t))) ; never gets here
1187 (defun forward-ifdef (&optional arg
)
1188 "Move point to beginning of line of the next ifdef-endif.
1189 With argument, do this that many times."
1191 (or arg
(setq arg
1))
1192 (if (< arg
0) (backward-ifdef (- arg
))
1195 (let ((start (point)))
1196 (unless (hif-looking-at-ifX)
1197 (hif-find-next-relevant))
1198 (if (hif-looking-at-ifX)
1199 (hif-ifdef-to-endif)
1201 (error "No following #ifdef"))))))
1204 (defun backward-ifdef (&optional arg
)
1205 "Move point to beginning of the previous ifdef-endif.
1206 With argument, do this that many times."
1208 (or arg
(setq arg
1))
1209 (if (< arg
0) (forward-ifdef (- arg
))
1213 (let ((start (point)))
1214 (unless (hif-looking-at-endif)
1215 (hif-find-previous-relevant))
1216 (if (hif-looking-at-endif)
1217 (hif-endif-to-ifdef)
1219 (error "No previous #ifdef"))))))
1222 (defun down-ifdef ()
1223 "Move point to beginning of nested ifdef or else-part."
1225 (let ((start (point)))
1226 (hif-find-next-relevant)
1227 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
1230 (error "No following #ifdef"))))
1234 "Move point to beginning of enclosing ifdef or else-part."
1237 (let ((start (point)))
1238 (unless (hif-looking-at-endif)
1239 (hif-find-previous-relevant))
1240 (if (hif-looking-at-endif)
1241 (hif-endif-to-ifdef))
1242 (if (= start
(point))
1243 (error "No previous #ifdef"))))
1245 (defun next-ifdef (&optional arg
)
1246 "Move to the beginning of the next #ifX, #else, or #endif.
1247 With argument, do this that many times."
1249 (or arg
(setq arg
1))
1250 (if (< arg
0) (previous-ifdef (- arg
))
1253 (hif-find-next-relevant)
1256 (error "No following #ifdefs, #elses, or #endifs")))))
1258 (defun previous-ifdef (&optional arg
)
1259 "Move to the beginning of the previous #ifX, #else, or #endif.
1260 With argument, do this that many times."
1262 (or arg
(setq arg
1))
1263 (if (< arg
0) (next-ifdef (- arg
))
1266 (let ((start (point)))
1267 (hif-find-previous-relevant)
1268 (if (= start
(point))
1269 (error "No previous #ifdefs, #elses, or #endifs"))))))
1272 ;;===%%SF%% parsing (End) ===
1275 ;;===%%SF%% hide-ifdef-hiding (Start) ===
1278 ;;; A range is a structure with four components:
1279 ;;; ELSE-P True if there was an else clause for the ifdef.
1280 ;;; START The start of the range. (beginning of line)
1281 ;;; ELSE The else marker (beginning of line)
1282 ;;; Only valid if ELSE-P is true.
1283 ;;; END The end of the range. (beginning of line)
1285 (defsubst hif-make-range
(start end
&optional else
)
1286 (list start else end
))
1288 (defsubst hif-range-start
(range) (elt range
0))
1289 (defsubst hif-range-else
(range) (elt range
1))
1290 (defsubst hif-range-end
(range) (elt range
2))
1295 ;;; The workhorse, it delimits the #if region. Reasonably simple:
1296 ;;; Skip until an #else or #endif is found, remembering positions. If
1297 ;;; an #else was found, skip some more, looking for the true #endif.
1299 (defun hif-find-range ()
1300 "Return a Range structure describing the current #if region.
1301 Point is left unchanged."
1302 ;; (message "hif-find-range at %d" (point))
1305 (let ((start (point))
1308 ;; Part one. Look for either #endif or #else.
1309 ;; This loop-and-a-half dedicated to E. Dijkstra.
1311 (hif-find-next-relevant)
1312 (hif-looking-at-ifX)) ; Skip nested ifdef
1313 (hif-ifdef-to-endif))
1314 ;; Found either a #else or an #endif.
1315 (cond ((hif-looking-at-else)
1316 (setq else
(point)))
1318 (setq end
(point)))) ; (line-end-position)
1319 ;; If found #else, look for #endif.
1322 (hif-find-next-relevant)
1323 (hif-looking-at-ifX)) ; Skip nested ifdef
1324 (hif-ifdef-to-endif))
1325 (if (hif-looking-at-else)
1326 (error "Found two elses in a row? Broken!"))
1327 (setq end
(point))) ; (line-end-position)
1328 (hif-make-range start end else
))))
1333 (defun hif-hide-line (point)
1334 "Hide the line containing point.
1335 Does nothing if `hide-ifdef-lines' is nil."
1336 (when hide-ifdef-lines
1339 (hide-ifdef-region-internal
1340 (line-beginning-position) (progn (hif-end-of-line) (point))))))
1343 ;;; Hif-Possibly-Hide
1344 ;;; There are four cases. The #ifX expression is "taken" if it
1345 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
1346 ;;; inside the #ifdef would be included when the program was
1349 ;;; Case 1: #ifX taken, and there's an #else.
1350 ;;; The #else part must be hidden. The #if (then) part must be
1351 ;;; processed for nested #ifX's.
1352 ;;; Case 2: #ifX taken, and there's no #else.
1353 ;;; The #if part must be processed for nested #ifX's.
1354 ;;; Case 3: #ifX not taken, and there's an #else.
1355 ;;; The #if part must be hidden. The #else part must be processed
1356 ;;; for nested #ifs.
1357 ;;; Case 4: #ifX not taken, and there's no #else.
1358 ;;; The #ifX part must be hidden.
1360 ;;; Further processing is done by narrowing to the relevant region
1361 ;;; and just recursively calling hide-ifdef-guts.
1363 ;;; When hif-possibly-hide returns, point is at the end of the
1364 ;;; possibly-hidden range.
1366 (defun hif-recurse-on (start end
)
1367 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
1372 (narrow-to-region (point) end
)
1373 (hide-ifdef-guts))))
1375 (defun hif-possibly-hide ()
1376 "Called at #ifX expression, this hides those parts that should be hidden.
1377 It uses the judgment of `hide-ifdef-evaluator'."
1378 ;; (message "hif-possibly-hide") (sit-for 1)
1379 (let ((test (hif-canonicalize hif-ifx-regexp
))
1380 (range (hif-find-range)))
1381 ;; (message "test = %s" test) (sit-for 1)
1383 (hif-hide-line (hif-range-end range
))
1384 (if (not (hif-not (funcall hide-ifdef-evaluator test
)))
1385 (cond ((hif-range-else range
) ; case 1
1386 (hif-hide-line (hif-range-else range
))
1387 (hide-ifdef-region (hif-range-else range
)
1388 (1- (hif-range-end range
)))
1389 (hif-recurse-on (hif-range-start range
)
1390 (hif-range-else range
)))
1392 (hif-recurse-on (hif-range-start range
)
1393 (hif-range-end range
))))
1394 (cond ((hif-range-else range
) ; case 3
1395 (hif-hide-line (hif-range-else range
))
1396 (hide-ifdef-region (hif-range-start range
)
1397 (1- (hif-range-else range
)))
1398 (hif-recurse-on (hif-range-else range
)
1399 (hif-range-end range
)))
1401 (hide-ifdef-region (point)
1402 (1- (hif-range-end range
))))))
1403 (hif-hide-line (hif-range-start range
)) ; Always hide start.
1404 (goto-char (hif-range-end range
))
1407 (defun hif-parse-macro-arglist (str)
1408 "Parse argument list formatted as '( arg1 [ , argn] [...] )'.
1409 The '...' is also included. Return a list of the arguments, if '...' exists the
1410 first arg will be `hif-etc'."
1411 (let* ((hif-simple-token-only nil
) ; Dynamic binding var for `hif-tokenize'
1414 (- (point) (length str
)) (point)))) ; Remove `hif-lparen'
1416 (while (not (eq (setq token
(pop tokenlist
)) 'hif-rparen
))
1418 ((eq token
'hif-etc
)
1420 ((eq token
'hif-comma
)
1423 (push token result
))))
1425 (cons 'hif-etc
(nreverse result
))
1426 (nreverse result
))))
1428 ;; The original version of hideif evaluates the macro early and store the
1429 ;; final values for the defined macro into the symbol database (aka
1430 ;; `hide-ifdef-env'). The evaluation process is "strings -> tokens -> parsed
1431 ;; tree -> [value]". (The square bracket refers to what's stored in in our
1432 ;; `hide-ifdef-env'.)
1434 ;; This forbids the evaluation of an argumented macro since the parameters
1435 ;; are applied at run time. In order to support argumented macro I then
1436 ;; postponed the evaluation process one stage and store the "parsed tree"
1437 ;; into symbol database. The evaluation process was then "strings -> tokens
1438 ;; -> [parsed tree] -> value". Hideif therefore run slower since it need to
1439 ;; evaluate the parsed tree everytime when trying to expand the symbol. These
1440 ;; temporarily code changes are obsolete and not in Emacs source repository.
1442 ;; Furthermore, CPP did allow partial expression to be defined in several
1443 ;; macros and later got concatenated into a complete expression and then
1444 ;; evaluate it. In order to match this behavior I had to postpone one stage
1445 ;; further, otherwise those partial expression will be fail on parsing and
1446 ;; we'll miss all macros that reference it. The evaluation process thus
1447 ;; became "strings -> [tokens] -> parsed tree -> value." This degraded the
1448 ;; performance since we need to parse tokens and evaluate them everytime
1449 ;; when that symbol is referenced.
1451 ;; In real cases I found a lot portion of macros are "simple macros" that
1452 ;; expand to literals like integers or other symbols. In order to enhance
1453 ;; the performance I use this `hif-simple-token-only' to notify my code and
1454 ;; save the final [value] into symbol database. [lukelee]
1456 (defun hif-find-define (&optional min max
)
1457 "Parse texts and retrieve all defines within the region MIN and MAX."
1459 (and min
(goto-char min
))
1460 (and (re-search-forward hif-define-regexp max t
)
1462 (let* ((defining (string= "define" (match-string 2)))
1463 (name (and (re-search-forward hif-macroref-regexp max t
)
1466 (parmlist (and (match-string 3) ; First arg id found
1467 (hif-parse-macro-arglist (match-string 2)))))
1469 ;; Ignore name (still need to return 't), or define the name
1470 (or (and hide-ifdef-exclude-define-regexp
1471 (string-match hide-ifdef-exclude-define-regexp
1474 (let* ((start (point))
1475 (end (progn (hif-end-of-line) (point)))
1476 (hif-simple-token-only nil
) ; Dynamic binding
1479 ;; `hif-simple-token-only' is set/clear
1480 ;; only in this block
1482 ;; Prevent C statements like
1483 ;; 'do { ... } while (0)'
1484 (hif-tokenize start end
)
1486 ;; We can't just return nil here since
1487 ;; this will stop hideif from searching
1488 ;; for more #defines.
1489 (setq hif-simple-token-only t
)
1490 (buffer-substring-no-properties
1492 ;; For simple tokens we save only the parsed result;
1493 ;; otherwise we save the tokens and parse it after
1494 ;; parameter replacement
1496 ;; `hif-simple-token-only' is checked only
1498 (or (and hif-simple-token-only
1500 (= (length tokens
) 1)
1501 (hif-parse-exp tokens
))
1502 `(hif-define-macro ,parmlist
1505 (assoc (intern name
) hide-ifdef-env
))))
1508 (or (setcdr SA expr
) t
)
1509 ;; Lazy evaluation, eval only if hif-lookup find it.
1510 ;; Define it anyway, even if nil it's still in list
1511 ;; and therefore considerred defined
1512 (push (cons (intern name
) expr
) hide-ifdef-env
)))))
1515 (hif-undefine-symbol (intern name
))))))
1519 (defun hif-add-new-defines (&optional min max
)
1520 "Scan and add all #define macros between MIN and MAX."
1524 ;; (mark-region min max) ;; for debugging
1525 (while (hif-find-define min max
)
1527 (if max
(goto-char max
)
1528 (goto-char (point-max))))))
1530 (defun hide-ifdef-guts ()
1531 "Does most of the work of `hide-ifdefs'.
1532 It does not do the work that's pointless to redo on a recursive entry."
1533 ;; (message "hide-ifdef-guts")
1535 (let ((case-fold-search nil
)
1537 (goto-char (point-min))
1540 (setf max
(hif-find-any-ifX))
1541 (hif-add-new-defines min max
)
1543 (hif-possibly-hide))
1547 ;;===%%SF%% hide-ifdef-hiding (End) ===
1550 ;;===%%SF%% exports (Start) ===
1552 (defun hide-ifdef-toggle-read-only ()
1553 "Toggle `hide-ifdef-read-only'."
1555 (setq hide-ifdef-read-only
(not hide-ifdef-read-only
))
1556 (message "Hide-Read-Only %s"
1557 (if hide-ifdef-read-only
"ON" "OFF"))
1558 (if hide-ifdef-hiding
1559 (setq buffer-read-only
(or hide-ifdef-read-only
1560 hif-outside-read-only
)))
1561 (force-mode-line-update))
1563 (defun hide-ifdef-toggle-outside-read-only ()
1564 "Replacement for `toggle-read-only' within Hide-Ifdef mode."
1566 (setq hif-outside-read-only
(not hif-outside-read-only
))
1567 (message "Read only %s"
1568 (if hif-outside-read-only
"ON" "OFF"))
1569 (setq buffer-read-only
1570 (or (and hide-ifdef-hiding hide-ifdef-read-only
)
1571 hif-outside-read-only
))
1572 (force-mode-line-update))
1574 (defun hide-ifdef-toggle-shadowing ()
1577 (set (make-local-variable 'hide-ifdef-shadow
) (not hide-ifdef-shadow
))
1578 (message "Shadowing %s" (if hide-ifdef-shadow
"ON" "OFF"))
1581 (dolist (overlay (overlays-in (point-min) (point-max)))
1582 (when (overlay-get overlay
'hide-ifdef
)
1583 (if hide-ifdef-shadow
1585 (overlay-put overlay
'invisible nil
)
1586 (overlay-put overlay
'face
'hide-ifdef-shadow
))
1587 (overlay-put overlay
'face nil
)
1588 (overlay-put overlay
'invisible
'hide-ifdef
))))))
1590 (defun hide-ifdef-define (var)
1591 "Define a VAR so that #ifdef VAR would be included."
1592 (interactive "SDefine what? ")
1594 (if hide-ifdef-hiding
(hide-ifdefs)))
1596 (defun hif-undefine-symbol (var)
1597 (setq hide-ifdef-env
1598 (delete (assoc var hide-ifdef-env
) hide-ifdef-env
)))
1601 (defun hide-ifdef-undef (start end
)
1602 "Undefine a VAR so that #ifdef VAR would not be included."
1605 (or (and mark-active
1606 (buffer-substring-no-properties start end
))
1607 (read-string "Undefine what? " (current-word))))
1610 (if (zerop (hif-defined sym
))
1611 (message "`%s' not defined, no need to undefine it" symstr
)
1612 (hif-undefine-symbol sym
)
1613 (if hide-ifdef-hiding
(hide-ifdefs))
1614 (message "`%S' undefined" sym
))))
1616 (defun hide-ifdefs (&optional nomsg
)
1617 "Hide the contents of some #ifdefs.
1618 Assume that defined symbols have been added to `hide-ifdef-env'.
1619 The text hidden is the text that would not be included by the C
1620 preprocessor if it were given the file with those symbols defined.
1622 Turn off hiding by calling `show-ifdefs'."
1625 (message "Hiding...")
1626 (setq hif-outside-read-only buffer-read-only
)
1627 (unless hide-ifdef-mode
(hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
1628 (if hide-ifdef-hiding
1629 (show-ifdefs)) ; Otherwise, deep confusion.
1630 (setq hide-ifdef-hiding t
)
1632 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
))
1634 (message "Hiding done")))
1637 (defun show-ifdefs ()
1638 "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
1640 (setq buffer-read-only hif-outside-read-only
)
1642 (setq hide-ifdef-hiding nil
))
1645 (defun hif-find-ifdef-block ()
1646 "Utility to hide and show ifdef block.
1647 Return as (TOP . BOTTOM) the extent of ifdef block."
1649 (cons (save-excursion
1651 (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
1654 (hif-ifdef-to-endif)
1655 (setq max-bottom
(1- (point)))))
1658 (unless (hif-looking-at-endif)
1659 (hif-find-next-relevant))
1660 (while (hif-looking-at-ifX)
1661 (hif-ifdef-to-endif)
1662 (hif-find-next-relevant))
1663 (min max-bottom
(1- (point)))))))
1666 (defun hide-ifdef-block ()
1667 "Hide the ifdef block (true or false part) enclosing or before the cursor."
1669 (unless hide-ifdef-mode
(hide-ifdef-mode 1))
1670 (let ((top-bottom (hif-find-ifdef-block)))
1671 (hide-ifdef-region (car top-bottom
) (cdr top-bottom
))
1672 (when hide-ifdef-lines
1673 (hif-hide-line (car top-bottom
))
1674 (hif-hide-line (1+ (cdr top-bottom
))))
1675 (setq hide-ifdef-hiding t
))
1676 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
)))
1678 (defun show-ifdef-block ()
1679 "Show the ifdef block (true or false part) enclosing or before the cursor."
1681 (let ((top-bottom (hif-find-ifdef-block)))
1682 (if hide-ifdef-lines
1683 (hif-show-ifdef-region
1685 (goto-char (car top-bottom
)) (line-beginning-position))
1687 (goto-char (1+ (cdr top-bottom
)))
1688 (hif-end-of-line) (point)))
1689 (hif-show-ifdef-region (1- (car top-bottom
)) (cdr top-bottom
)))))
1692 ;;; definition alist support
1694 (defvar hide-ifdef-define-alist nil
1695 "A global assoc list of pre-defined symbol lists.")
1697 (defun hif-compress-define-list (env)
1698 "Compress the define list ENV into a list of defined symbols only."
1699 (let ((new-defs nil
))
1700 (dolist (def env new-defs
)
1701 (if (hif-lookup (car def
)) (push (car def
) new-defs
)))))
1703 (defun hide-ifdef-set-define-alist (name)
1704 "Set the association for NAME to `hide-ifdef-env'."
1705 (interactive "SSet define list: ")
1706 (push (cons name
(hif-compress-define-list hide-ifdef-env
))
1707 hide-ifdef-define-alist
))
1709 (defun hide-ifdef-use-define-alist (name)
1710 "Set `hide-ifdef-env' to the define list specified by NAME."
1712 (list (completing-read "Use define list: "
1713 (mapcar (lambda (x) (symbol-name (car x
)))
1714 hide-ifdef-define-alist
)
1716 (if (stringp name
) (setq name
(intern name
)))
1717 (let ((define-list (assoc name hide-ifdef-define-alist
)))
1719 (setq hide-ifdef-env
1720 (mapcar (lambda (arg) (cons arg t
))
1722 (error "No define list for %s" name
))
1723 (if hide-ifdef-hiding
(hide-ifdefs))))
1727 ;;; hideif.el ends here