1 ;;; hideif.el --- hides selected code within ifdef -*- lexical-binding:t -*-
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 (cl-caddr tokens
) 'hif-rparen
))
554 (setq tokens
(cl-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)))
613 (hif-expand-token-list
614 (hif-macro-supply-arguments tok parmlist
)
616 (setq remains
(cons hif-token hif-token-list
))
618 ;; Argument list is nil, direct expansion
619 (setq rep
(hif-expand-token-list
620 (cl-caddr rep
) ; Macro's token list
622 ;; Replace all remaining references immediately
623 (setq remains
(cl-substitute tok rep remains
))
625 ;; Lookup tok returns an atom
628 ;;[2013-10-22 16:06:12 +0800] Must keep the token, removing
629 ;; this token might results in an incomplete expression that
630 ;; cannot be parsed further.
631 ;;((= 1 (hif-defined tok)) ; defined (hif-defined tok)=1,
632 ;; ;;but empty (hif-lookup tok)=nil, thus remove this token
633 ;; (setq remains (delete tok remains))
641 (hif-flatten (nreverse expanded
)))))
643 (defun hif-parse-exp (token-list &optional macroname
)
644 "Parse the TOKEN-LIST.
645 Return translated list in prefix form. MACRONAME is applied when invoking
646 macros to prevent self-reference."
647 (let ((hif-token-list (hif-expand-token-list token-list macroname
)))
652 (if hif-token
; is there still a token?
653 (error "Error: unexpected token: %s" hif-token
)))))
655 (defun hif-exprlist ()
656 "Parse an exprlist: expr { ',' expr}."
657 (let ((result (hif-expr)))
658 (if (eq hif-token
'hif-comma
)
659 (let ((temp (list result
)))
663 (push (hif-expr) temp
)
664 (eq hif-token
'hif-comma
)))
665 (cons 'hif-comma
(nreverse temp
)))
669 "Parse an expression as found in #if.
670 expr : or-expr | or-expr '?' expr ':' expr."
671 (let ((result (hif-or-expr))
673 (while (eq hif-token
'hif-conditional
)
675 (setq middle
(hif-expr))
676 (if (eq hif-token
'hif-colon
)
679 (setq result
(list 'hif-conditional result middle
(hif-expr))))
680 (error "Error: unexpected token: %s" hif-token
)))
683 (defun hif-or-expr ()
684 "Parse an or-expr : and-expr | or-expr '||' and-expr."
685 (let ((result (hif-and-expr)))
686 (while (eq hif-token
'hif-or
)
688 (setq result
(list 'hif-or result
(hif-and-expr))))
691 (defun hif-and-expr ()
692 "Parse an and-expr : logior-expr | and-expr '&&' logior-expr."
693 (let ((result (hif-logior-expr)))
694 (while (eq hif-token
'hif-and
)
696 (setq result
(list 'hif-and result
(hif-logior-expr))))
699 (defun hif-logior-expr ()
700 "Parse a logor-expr : logxor-expr | logor-expr '|' logxor-expr."
701 (let ((result (hif-logxor-expr)))
702 (while (eq hif-token
'hif-logior
)
704 (setq result
(list 'hif-logior result
(hif-logxor-expr))))
707 (defun hif-logxor-expr ()
708 "Parse a logxor-expr : logand-expr | logxor-expr '^' logand-expr."
709 (let ((result (hif-logand-expr)))
710 (while (eq hif-token
'hif-logxor
)
712 (setq result
(list 'hif-logxor result
(hif-logand-expr))))
715 (defun hif-logand-expr ()
716 "Parse a logand-expr : eq-expr | logand-expr '&' eq-expr."
717 (let ((result (hif-eq-expr)))
718 (while (eq hif-token
'hif-logand
)
720 (setq result
(list 'hif-logand result
(hif-eq-expr))))
723 (defun hif-eq-expr ()
724 "Parse an eq-expr : comp | eq-expr `=='|`!=' comp."
725 (let ((result (hif-comp-expr))
727 (while (memq hif-token
'(hif-equal hif-notequal
))
728 (setq eq-token hif-token
)
730 (setq result
(list eq-token result
(hif-comp-expr))))
733 (defun hif-comp-expr ()
734 "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
735 (let ((result (hif-logshift-expr))
737 (while (memq hif-token
'(hif-greater hif-less hif-greater-equal
739 (setq comp-token hif-token
)
741 (setq result
(list comp-token result
(hif-logshift-expr))))
744 (defun hif-logshift-expr ()
745 "Parse a logshift : math | logshift `<<'|`>>' math."
746 (let ((result (hif-math))
748 (while (memq hif-token
'(hif-shiftleft hif-shiftright
))
749 (setq shift-token hif-token
)
751 (setq result
(list shift-token result
(hif-math))))
755 "Parse an expression with + or -.
756 math : muldiv | math '+|-' muldiv."
757 (let ((result (hif-muldiv-expr))
759 (while (memq hif-token
'(hif-plus hif-minus
))
760 (setq math-op hif-token
)
762 (setq result
(list math-op result
(hif-muldiv-expr))))
765 (defun hif-muldiv-expr ()
766 "Parse an expression with *,/,%.
767 muldiv : factor | muldiv '*|/|%' factor."
768 (let ((result (hif-factor))
770 (while (memq hif-token
'(hif-multiply hif-divide hif-modulo
))
771 (setq math-op hif-token
)
773 (setq result
(list math-op result
(hif-factor))))
778 factor : '!' factor | '~' factor | '(' expr ')' | 'defined(' id ')' |
779 'id(parmlist)' | strings | id."
781 ((eq hif-token
'hif-not
)
783 (list 'hif-not
(hif-factor)))
785 ((eq hif-token
'hif-lognot
)
787 (list 'hif-lognot
(hif-factor)))
789 ((eq hif-token
'hif-lparen
)
791 (let ((result (hif-exprlist)))
792 (if (not (eq hif-token
'hif-rparen
))
793 (error "Bad token in parenthesized expression: %s" hif-token
)
797 ((eq hif-token
'hif-defined
)
799 (let ((paren (when (eq hif-token
'hif-lparen
) (hif-nexttoken) t
))
801 (if (memq hif-token
'(or and not hif-defined hif-lparen hif-rparen
))
802 (error "Error: unexpected token: %s" hif-token
))
805 (unless (eq hif-token
'hif-rparen
)
806 (error "Error: expected \")\" after identifier")))
808 `(hif-defined (quote ,ident
))))
811 (prog1 hif-token
(hif-nexttoken)))
813 (hif-string-concatenation))
816 ((memq hif-token
'(hif-minus hif-plus
))
817 (list (prog1 hif-token
(hif-nexttoken)) 0 (hif-factor)))
820 (let ((ident hif-token
))
822 (if (eq hif-token
'hif-lparen
)
823 (hif-place-macro-invocation ident
)
824 `(hif-lookup (quote ,ident
)))))))
826 (defun hif-get-argument-list ()
828 (parmlist nil
) ; A "token" list of parameters, will later be parsed
831 (while (or (not (eq (hif-nexttoken) 'hif-rparen
))
833 (if (eq (car (last parm
)) 'hif-comma
)
836 ((eq hif-token
'hif-lparen
)
837 (setq nest
(1+ nest
)))
838 ((eq hif-token
'hif-rparen
)
839 (setq nest
(1- nest
)))
840 ((and (eq hif-token
'hif-comma
)
842 (push (nreverse parm
) parmlist
)
844 (push hif-token parm
))
846 (push (nreverse parm
) parmlist
) ; Okay even if PARM is nil
847 (hif-nexttoken) ; Drop the `hif-rparen', get next token
848 (nreverse parmlist
)))
850 (defun hif-place-macro-invocation (ident)
851 (let ((parmlist (hif-get-argument-list)))
852 `(hif-invoke (quote ,ident
) (quote ,parmlist
))))
854 (defun hif-string-concatenation ()
855 "Parse concatenated strings: string | strings string."
856 (let ((result (substring-no-properties hif-token
)))
857 (while (stringp (hif-nexttoken))
859 (substring result
0 -
1) ; remove trailing '"'
860 (substring hif-token
1)))) ; remove leading '"'
863 (defun hif-define-macro (_parmlist _token-body
)
864 "A marker for defined macro with arguments.
865 This macro cannot be evaluated alone without parameters inputed."
866 ;;TODO: input arguments at run time, use minibuffer to query all arguments
868 "Argumented macro cannot be evaluated without passing any parameter"))
870 (defun hif-stringify (a)
871 "Stringify a number, string or symbol."
874 (number-to-string a
))
878 (concat "\"" a
"\""))
880 (error "Invalid token to stringify"))))
882 (defun intern-safe (str)
886 (defun hif-token-concat (a b
)
887 "Concatenate two tokens into a longer token.
888 Currently support only simple token concatenation. Also support weird (but
889 valid) token concatenation like '>' ## '>' becomes '>>'. Here we take care only
890 those that can be evaluated during preprocessing time and ignore all those that
891 can only be evaluated at C(++) runtime (like '++', '--' and '+='...)."
892 (if (or (memq a hif-valid-token-list
)
893 (memq b hif-valid-token-list
))
894 (let* ((ra (car (rassq a hif-token-alist
)))
895 (rb (car (rassq b hif-token-alist
)))
897 (cdr (assoc (concat ra rb
) hif-token-alist
)))))
899 ;;(error "Invalid token to concatenate")
900 (error "Concatenating \"%s\" and \"%s\" does not give a valid \
902 (or ra
(symbol-name a
))
903 (or rb
(symbol-name b
)))))
904 (intern-safe (concat (hif-stringify a
)
905 (hif-stringify b
)))))
907 (defun hif-mathify (val)
908 "Treat VAL as a number: if it's t or nil, use 1 or 0."
913 (defun hif-conditional (a b c
)
914 (if (not (zerop (hif-mathify a
))) (hif-mathify b
) (hif-mathify c
)))
916 (and (not (zerop (hif-mathify a
))) (not (zerop (hif-mathify b
)))))
918 (or (not (zerop (hif-mathify a
))) (not (zerop (hif-mathify b
)))))
920 (zerop (hif-mathify a
)))
921 (defun hif-lognot (a)
922 (lognot (hif-mathify a
)))
924 (defmacro hif-mathify-binop
(fun)
926 ,(format "Like `%s' but treat t and nil as 1 and 0." fun
)
927 (,fun
(hif-mathify a
) (hif-mathify b
))))
929 (defun hif-shiftleft (a b
)
930 (setq a
(hif-mathify a
))
931 (setq b
(hif-mathify b
))
936 (defun hif-shiftright (a b
)
937 (setq a
(hif-mathify a
))
938 (setq b
(hif-mathify b
))
944 (defalias 'hif-multiply
(hif-mathify-binop *))
945 (defalias 'hif-divide
(hif-mathify-binop /))
946 (defalias 'hif-modulo
(hif-mathify-binop %
))
947 (defalias 'hif-plus
(hif-mathify-binop +))
948 (defalias 'hif-minus
(hif-mathify-binop -
))
949 (defalias 'hif-equal
(hif-mathify-binop =))
950 (defalias 'hif-notequal
(hif-mathify-binop /=))
951 (defalias 'hif-greater
(hif-mathify-binop >))
952 (defalias 'hif-less
(hif-mathify-binop <))
953 (defalias 'hif-greater-equal
(hif-mathify-binop >=))
954 (defalias 'hif-less-equal
(hif-mathify-binop <=))
955 (defalias 'hif-logior
(hif-mathify-binop logior
))
956 (defalias 'hif-logxor
(hif-mathify-binop logxor
))
957 (defalias 'hif-logand
(hif-mathify-binop logand
))
960 (defun hif-comma (&rest expr
)
961 "Evaluate a list of EXPR, return the result of the last item."
965 (setq result
(funcall hide-ifdef-evaluator e
))))
968 (defun hif-token-stringification (l)
969 "Scan token list for `hif-stringify' ('#') token and stringify the next token."
972 (push (if (eq (car l
) 'hif-stringify
)
975 (hif-stringify (cadr l
))
976 (error "No token to stringify"))
983 (defun hif-token-concatenation (l)
984 "Scan token list for `hif-token-concat' ('##') token and concatenate two tokens."
988 (while (eq (car l
) 'hif-token-concat
)
990 (error "No token before ## to concatenate"))
992 (error "No token after ## to concatenate"))
993 (setq prev
(hif-token-concat prev
(cadr l
)))
996 (setq result
(append result
(list prev
))))
1000 (append result
(list prev
))
1003 (defun hif-delimit (lis atom
)
1004 (nconc (cl-mapcan (lambda (l) (list l atom
))
1008 ;; Perform token replacement:
1009 (defun hif-macro-supply-arguments (macro-name actual-parms
)
1010 "Expand a macro call, replace ACTUAL-PARMS in the macro body."
1011 (let* ((SA (assoc macro-name hide-ifdef-env
))
1014 (eq (cadr SA
) 'hif-define-macro
)
1016 (formal-parms (and macro
(car macro
)))
1017 (macro-body (and macro
(cadr macro
)))
1023 (when (and actual-parms formal-parms macro-body
)
1024 ;; For each actual parameter, evaluate each one and associate it
1025 ;; with an actual parameter, put it into local table and finally
1026 ;; evaluate the macro body.
1027 (if (setq etc
(eq (car formal-parms
) 'hif-etc
))
1028 ;; Take care of `hif-etc' first. Prefix `hif-comma' back if needed.
1029 (setq formal-parms
(cdr formal-parms
)))
1030 (setq formal-count
(length formal-parms
)
1031 actual-count
(length actual-parms
))
1033 (if (> formal-count actual-count
)
1034 (error "Too few parmameter for macro %S" macro-name
)
1035 (if (< formal-count actual-count
)
1037 (error "Too many parameters for macro %S" macro-name
))))
1039 ;; Perform token replacement on the MACRO-BODY with the parameters
1040 (while (setq formal
(pop formal-parms
))
1041 ;; Prevent repetitive substitutation, thus cannot use `subst'
1043 ;; #define mac(a,b) (a+b)
1044 ;; #define testmac mac(b,y)
1045 ;; testmac should expand to (b+y): replace of argument a and b
1046 ;; occurs simultaneously, not sequentially. If sequentially,
1047 ;; according to the argument order, it will become:
1048 ;; 1. formal parm #1 'a' replaced by actual parm 'b', thus (a+b)
1050 ;; 2. formal parm #2 'b' replaced by actual parm 'y', thus (b+b)
1053 ;; Unlike `subst', `substitute' replace only the top level
1054 ;; instead of the whole tree; more importantly, it's not
1056 (cl-substitute (if (and etc
(null formal-parms
))
1057 (hif-delimit actual-parms
'hif-comma
)
1060 (setq actual-parms
(cdr actual-parms
)))
1062 ;; Replacement completed, flatten the whole token list
1063 (setq macro-body
(hif-flatten macro-body
))
1065 ;; Stringification and token concatenation happens here
1066 (hif-token-concatenation (hif-token-stringification macro-body
)))))
1068 (defun hif-invoke (macro-name actual-parms
)
1069 "Invoke a macro by expanding it, reparse macro-body and finally invoke it."
1070 ;; Reparse the macro body and evaluate it
1071 (funcall hide-ifdef-evaluator
1073 (hif-macro-supply-arguments macro-name actual-parms
)
1076 ;;;----------- end of parser -----------------------
1079 (defun hif-canonicalize-tokens (regexp) ; For debugging
1080 "Return the expanded result of the scanned tokens."
1082 (re-search-forward regexp
)
1083 (let* ((curr-regexp (match-string 0))
1084 (defined (string-match hif-ifxdef-regexp curr-regexp
))
1085 (negate (and defined
1086 (string= (match-string 2 curr-regexp
) "n")))
1087 (hif-simple-token-only nil
) ;; Dynamic binding var for `hif-tokenize'
1088 (tokens (hif-tokenize (point)
1089 (progn (hif-end-of-line) (point)))))
1091 (setq tokens
(list 'hif-defined tokens
)))
1093 (setq tokens
(list 'hif-not tokens
)))
1096 (defun hif-canonicalize (regexp)
1097 "Return a Lisp expression for its condition by scanning current buffer.
1098 Do this when cursor is at the beginning of `regexp' (i.e. #ifX)."
1099 (let ((case-fold-search nil
))
1101 (re-search-forward regexp
)
1102 (let* ((curr-regexp (match-string 0))
1103 (defined (string-match hif-ifxdef-regexp curr-regexp
))
1104 (negate (and defined
1105 (string= (match-string 2 curr-regexp
) "n")))
1106 (hif-simple-token-only nil
) ; Dynamic binding for `hif-tokenize'
1107 (tokens (hif-tokenize (point)
1108 (progn (hif-end-of-line) (point)))))
1110 (setq tokens
(list 'hif-defined tokens
)))
1112 (setq tokens
(list 'hif-not tokens
)))
1113 (hif-parse-exp tokens
)))))
1115 (defun hif-find-any-ifX ()
1116 "Move to next #if..., or #ifndef, at point or after."
1117 ;; (message "find ifX at %d" (point))
1119 (re-search-forward hif-ifx-regexp
(point-max) t
)
1120 (beginning-of-line)))
1123 (defun hif-find-next-relevant ()
1124 "Move to next #if..., #elif..., #else, or #endif, after the current line."
1125 ;; (message "hif-find-next-relevant at %d" (point))
1127 ;; Avoid infinite recursion by only going to line-beginning if match found
1128 (if (re-search-forward hif-ifx-else-endif-regexp
(point-max) t
)
1129 (beginning-of-line)))
1131 (defun hif-find-previous-relevant ()
1132 "Move to previous #if..., #else, or #endif, before the current line."
1133 ;; (message "hif-find-previous-relevant at %d" (point))
1135 ;; Avoid infinite recursion by only going to line-beginning if match found
1136 (if (re-search-backward hif-ifx-else-endif-regexp
(point-min) t
)
1137 (beginning-of-line)))
1140 (defun hif-looking-at-ifX ()
1141 (looking-at hif-ifx-regexp
)) ; Should eventually see #if
1142 (defun hif-looking-at-endif ()
1143 (looking-at hif-endif-regexp
))
1144 (defun hif-looking-at-else ()
1145 (looking-at hif-else-regexp
))
1147 (defun hif-looking-at-elif ()
1148 (looking-at hif-elif-regexp
))
1151 (defun hif-ifdef-to-endif ()
1152 "If positioned at #ifX, #elif, or #else form, skip to corresponding #endif."
1153 ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
1154 (hif-find-next-relevant)
1155 (cond ((hif-looking-at-ifX)
1156 (hif-ifdef-to-endif) ; Find endif of nested if
1157 (hif-ifdef-to-endif)) ; Find outer endif or else
1158 ((hif-looking-at-elif)
1159 (hif-ifdef-to-endif))
1160 ((hif-looking-at-else)
1161 (hif-ifdef-to-endif)) ; Find endif following else
1162 ((hif-looking-at-endif)
1165 (error "Mismatched #ifdef #endif pair"))))
1168 (defun hif-endif-to-ifdef ()
1169 "If positioned at #endif form, skip backward to corresponding #ifX."
1170 ;; (message "hif-endif-to-ifdef at %d" (point))
1171 (let ((start (point)))
1172 (hif-find-previous-relevant)
1173 (if (= start
(point))
1174 (error "Mismatched #ifdef #endif pair")))
1175 (cond ((hif-looking-at-endif)
1176 (hif-endif-to-ifdef) ; find beginning of nested if
1177 (hif-endif-to-ifdef)) ; find beginning of outer if or else
1178 ((hif-looking-at-else)
1179 (hif-endif-to-ifdef))
1180 ((hif-looking-at-ifX)
1182 (t))) ; never gets here
1185 (defun forward-ifdef (&optional arg
)
1186 "Move point to beginning of line of the next ifdef-endif.
1187 With argument, do this that many times."
1189 (or arg
(setq arg
1))
1190 (if (< arg
0) (backward-ifdef (- arg
))
1193 (let ((start (point)))
1194 (unless (hif-looking-at-ifX)
1195 (hif-find-next-relevant))
1196 (if (hif-looking-at-ifX)
1197 (hif-ifdef-to-endif)
1199 (error "No following #ifdef"))))))
1202 (defun backward-ifdef (&optional arg
)
1203 "Move point to beginning of the previous ifdef-endif.
1204 With argument, do this that many times."
1206 (or arg
(setq arg
1))
1207 (if (< arg
0) (forward-ifdef (- arg
))
1211 (let ((start (point)))
1212 (unless (hif-looking-at-endif)
1213 (hif-find-previous-relevant))
1214 (if (hif-looking-at-endif)
1215 (hif-endif-to-ifdef)
1217 (error "No previous #ifdef"))))))
1220 (defun down-ifdef ()
1221 "Move point to beginning of nested ifdef or else-part."
1223 (let ((start (point)))
1224 (hif-find-next-relevant)
1225 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
1228 (error "No following #ifdef"))))
1232 "Move point to beginning of enclosing ifdef or else-part."
1235 (let ((start (point)))
1236 (unless (hif-looking-at-endif)
1237 (hif-find-previous-relevant))
1238 (if (hif-looking-at-endif)
1239 (hif-endif-to-ifdef))
1240 (if (= start
(point))
1241 (error "No previous #ifdef"))))
1243 (defun next-ifdef (&optional arg
)
1244 "Move to the beginning of the next #ifX, #else, or #endif.
1245 With argument, do this that many times."
1247 (or arg
(setq arg
1))
1248 (if (< arg
0) (previous-ifdef (- arg
))
1251 (hif-find-next-relevant)
1254 (error "No following #ifdefs, #elses, or #endifs")))))
1256 (defun previous-ifdef (&optional arg
)
1257 "Move to the beginning of the previous #ifX, #else, or #endif.
1258 With argument, do this that many times."
1260 (or arg
(setq arg
1))
1261 (if (< arg
0) (next-ifdef (- arg
))
1264 (let ((start (point)))
1265 (hif-find-previous-relevant)
1266 (if (= start
(point))
1267 (error "No previous #ifdefs, #elses, or #endifs"))))))
1270 ;;===%%SF%% parsing (End) ===
1273 ;;===%%SF%% hide-ifdef-hiding (Start) ===
1276 ;;; A range is a structure with four components:
1277 ;;; ELSE-P True if there was an else clause for the ifdef.
1278 ;;; START The start of the range. (beginning of line)
1279 ;;; ELSE The else marker (beginning of line)
1280 ;;; Only valid if ELSE-P is true.
1281 ;;; END The end of the range. (beginning of line)
1283 (defsubst hif-make-range
(start end
&optional else
)
1284 (list start else end
))
1286 (defsubst hif-range-start
(range) (elt range
0))
1287 (defsubst hif-range-else
(range) (elt range
1))
1288 (defsubst hif-range-end
(range) (elt range
2))
1293 ;;; The workhorse, it delimits the #if region. Reasonably simple:
1294 ;;; Skip until an #else or #endif is found, remembering positions. If
1295 ;;; an #else was found, skip some more, looking for the true #endif.
1297 (defun hif-find-range ()
1298 "Return a Range structure describing the current #if region.
1299 Point is left unchanged."
1300 ;; (message "hif-find-range at %d" (point))
1303 (let ((start (point))
1306 ;; Part one. Look for either #endif or #else.
1307 ;; This loop-and-a-half dedicated to E. Dijkstra.
1309 (hif-find-next-relevant)
1310 (hif-looking-at-ifX)) ; Skip nested ifdef
1311 (hif-ifdef-to-endif))
1312 ;; Found either a #else or an #endif.
1313 (cond ((hif-looking-at-else)
1314 (setq else
(point)))
1316 (setq end
(point)))) ; (line-end-position)
1317 ;; If found #else, look for #endif.
1320 (hif-find-next-relevant)
1321 (hif-looking-at-ifX)) ; Skip nested ifdef
1322 (hif-ifdef-to-endif))
1323 (if (hif-looking-at-else)
1324 (error "Found two elses in a row? Broken!"))
1325 (setq end
(point))) ; (line-end-position)
1326 (hif-make-range start end else
))))
1331 (defun hif-hide-line (point)
1332 "Hide the line containing point.
1333 Does nothing if `hide-ifdef-lines' is nil."
1334 (when hide-ifdef-lines
1337 (hide-ifdef-region-internal
1338 (line-beginning-position) (progn (hif-end-of-line) (point))))))
1341 ;;; Hif-Possibly-Hide
1342 ;;; There are four cases. The #ifX expression is "taken" if it
1343 ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
1344 ;;; inside the #ifdef would be included when the program was
1347 ;;; Case 1: #ifX taken, and there's an #else.
1348 ;;; The #else part must be hidden. The #if (then) part must be
1349 ;;; processed for nested #ifX's.
1350 ;;; Case 2: #ifX taken, and there's no #else.
1351 ;;; The #if part must be processed for nested #ifX's.
1352 ;;; Case 3: #ifX not taken, and there's an #else.
1353 ;;; The #if part must be hidden. The #else part must be processed
1354 ;;; for nested #ifs.
1355 ;;; Case 4: #ifX not taken, and there's no #else.
1356 ;;; The #ifX part must be hidden.
1358 ;;; Further processing is done by narrowing to the relevant region
1359 ;;; and just recursively calling hide-ifdef-guts.
1361 ;;; When hif-possibly-hide returns, point is at the end of the
1362 ;;; possibly-hidden range.
1364 (defun hif-recurse-on (start end
)
1365 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
1370 (narrow-to-region (point) end
)
1371 (hide-ifdef-guts))))
1373 (defun hif-possibly-hide ()
1374 "Called at #ifX expression, this hides those parts that should be hidden.
1375 It uses the judgment of `hide-ifdef-evaluator'."
1376 ;; (message "hif-possibly-hide") (sit-for 1)
1377 (let ((test (hif-canonicalize hif-ifx-regexp
))
1378 (range (hif-find-range)))
1379 ;; (message "test = %s" test) (sit-for 1)
1381 (hif-hide-line (hif-range-end range
))
1382 (if (not (hif-not (funcall hide-ifdef-evaluator test
)))
1383 (cond ((hif-range-else range
) ; case 1
1384 (hif-hide-line (hif-range-else range
))
1385 (hide-ifdef-region (hif-range-else range
)
1386 (1- (hif-range-end range
)))
1387 (hif-recurse-on (hif-range-start range
)
1388 (hif-range-else range
)))
1390 (hif-recurse-on (hif-range-start range
)
1391 (hif-range-end range
))))
1392 (cond ((hif-range-else range
) ; case 3
1393 (hif-hide-line (hif-range-else range
))
1394 (hide-ifdef-region (hif-range-start range
)
1395 (1- (hif-range-else range
)))
1396 (hif-recurse-on (hif-range-else range
)
1397 (hif-range-end range
)))
1399 (hide-ifdef-region (point)
1400 (1- (hif-range-end range
))))))
1401 (hif-hide-line (hif-range-start range
)) ; Always hide start.
1402 (goto-char (hif-range-end range
))
1405 (defun hif-parse-macro-arglist (str)
1406 "Parse argument list formatted as '( arg1 [ , argn] [...] )'.
1407 The '...' is also included. Return a list of the arguments, if '...' exists the
1408 first arg will be `hif-etc'."
1409 (let* ((hif-simple-token-only nil
) ; Dynamic binding var for `hif-tokenize'
1412 (- (point) (length str
)) (point)))) ; Remove `hif-lparen'
1414 (while (not (eq (setq token
(pop tokenlist
)) 'hif-rparen
))
1416 ((eq token
'hif-etc
)
1418 ((eq token
'hif-comma
)
1421 (push token result
))))
1423 (cons 'hif-etc
(nreverse result
))
1424 (nreverse result
))))
1426 ;; The original version of hideif evaluates the macro early and store the
1427 ;; final values for the defined macro into the symbol database (aka
1428 ;; `hide-ifdef-env'). The evaluation process is "strings -> tokens -> parsed
1429 ;; tree -> [value]". (The square bracket refers to what's stored in in our
1430 ;; `hide-ifdef-env'.)
1432 ;; This forbids the evaluation of an argumented macro since the parameters
1433 ;; are applied at run time. In order to support argumented macro I then
1434 ;; postponed the evaluation process one stage and store the "parsed tree"
1435 ;; into symbol database. The evaluation process was then "strings -> tokens
1436 ;; -> [parsed tree] -> value". Hideif therefore run slower since it need to
1437 ;; evaluate the parsed tree everytime when trying to expand the symbol. These
1438 ;; temporarily code changes are obsolete and not in Emacs source repository.
1440 ;; Furthermore, CPP did allow partial expression to be defined in several
1441 ;; macros and later got concatenated into a complete expression and then
1442 ;; evaluate it. In order to match this behavior I had to postpone one stage
1443 ;; further, otherwise those partial expression will be fail on parsing and
1444 ;; we'll miss all macros that reference it. The evaluation process thus
1445 ;; became "strings -> [tokens] -> parsed tree -> value." This degraded the
1446 ;; performance since we need to parse tokens and evaluate them everytime
1447 ;; when that symbol is referenced.
1449 ;; In real cases I found a lot portion of macros are "simple macros" that
1450 ;; expand to literals like integers or other symbols. In order to enhance
1451 ;; the performance I use this `hif-simple-token-only' to notify my code and
1452 ;; save the final [value] into symbol database. [lukelee]
1454 (defun hif-find-define (&optional min max
)
1455 "Parse texts and retrieve all defines within the region MIN and MAX."
1457 (and min
(goto-char min
))
1458 (and (re-search-forward hif-define-regexp max t
)
1460 (let* ((defining (string= "define" (match-string 2)))
1461 (name (and (re-search-forward hif-macroref-regexp max t
)
1463 (parmlist (and (match-string 3) ; First arg id found
1464 (hif-parse-macro-arglist (match-string 2)))))
1466 ;; Ignore name (still need to return 't), or define the name
1467 (or (and hide-ifdef-exclude-define-regexp
1468 (string-match hide-ifdef-exclude-define-regexp
1471 (let* ((start (point))
1472 (end (progn (hif-end-of-line) (point)))
1473 (hif-simple-token-only nil
) ; Dynamic binding
1476 ;; `hif-simple-token-only' is set/clear
1477 ;; only in this block
1479 ;; Prevent C statements like
1480 ;; 'do { ... } while (0)'
1481 (hif-tokenize start end
)
1483 ;; We can't just return nil here since
1484 ;; this will stop hideif from searching
1485 ;; for more #defines.
1486 (setq hif-simple-token-only t
)
1487 (buffer-substring-no-properties
1489 ;; For simple tokens we save only the parsed result;
1490 ;; otherwise we save the tokens and parse it after
1491 ;; parameter replacement
1493 ;; `hif-simple-token-only' is checked only
1495 (or (and hif-simple-token-only
1497 (= (length tokens
) 1)
1498 (hif-parse-exp tokens
))
1499 `(hif-define-macro ,parmlist
1502 (assoc (intern name
) hide-ifdef-env
))))
1505 (or (setcdr SA expr
) t
)
1506 ;; Lazy evaluation, eval only if hif-lookup find it.
1507 ;; Define it anyway, even if nil it's still in list
1508 ;; and therefore considerred defined
1509 (push (cons (intern name
) expr
) hide-ifdef-env
)))))
1512 (hif-undefine-symbol (intern name
))))))
1516 (defun hif-add-new-defines (&optional min max
)
1517 "Scan and add all #define macros between MIN and MAX."
1521 ;; (mark-region min max) ;; for debugging
1522 (while (hif-find-define min max
)
1524 (if max
(goto-char max
)
1525 (goto-char (point-max))))))
1527 (defun hide-ifdef-guts ()
1528 "Does most of the work of `hide-ifdefs'.
1529 It does not do the work that's pointless to redo on a recursive entry."
1530 ;; (message "hide-ifdef-guts")
1532 (let ((case-fold-search nil
)
1534 (goto-char (point-min))
1537 (setf max
(hif-find-any-ifX))
1538 (hif-add-new-defines min max
)
1540 (hif-possibly-hide))
1544 ;;===%%SF%% hide-ifdef-hiding (End) ===
1547 ;;===%%SF%% exports (Start) ===
1549 (defun hide-ifdef-toggle-read-only ()
1550 "Toggle `hide-ifdef-read-only'."
1552 (setq hide-ifdef-read-only
(not hide-ifdef-read-only
))
1553 (message "Hide-Read-Only %s"
1554 (if hide-ifdef-read-only
"ON" "OFF"))
1555 (if hide-ifdef-hiding
1556 (setq buffer-read-only
(or hide-ifdef-read-only
1557 hif-outside-read-only
)))
1558 (force-mode-line-update))
1560 (defun hide-ifdef-toggle-outside-read-only ()
1561 "Replacement for `toggle-read-only' within Hide-Ifdef mode."
1563 (setq hif-outside-read-only
(not hif-outside-read-only
))
1564 (message "Read only %s"
1565 (if hif-outside-read-only
"ON" "OFF"))
1566 (setq buffer-read-only
1567 (or (and hide-ifdef-hiding hide-ifdef-read-only
)
1568 hif-outside-read-only
))
1569 (force-mode-line-update))
1571 (defun hide-ifdef-toggle-shadowing ()
1574 (set (make-local-variable 'hide-ifdef-shadow
) (not hide-ifdef-shadow
))
1575 (message "Shadowing %s" (if hide-ifdef-shadow
"ON" "OFF"))
1578 (dolist (overlay (overlays-in (point-min) (point-max)))
1579 (when (overlay-get overlay
'hide-ifdef
)
1580 (if hide-ifdef-shadow
1582 (overlay-put overlay
'invisible nil
)
1583 (overlay-put overlay
'face
'hide-ifdef-shadow
))
1584 (overlay-put overlay
'face nil
)
1585 (overlay-put overlay
'invisible
'hide-ifdef
))))))
1587 (defun hide-ifdef-define (var)
1588 "Define a VAR so that #ifdef VAR would be included."
1589 (interactive "SDefine what? ")
1591 (if hide-ifdef-hiding
(hide-ifdefs)))
1593 (defun hif-undefine-symbol (var)
1594 (setq hide-ifdef-env
1595 (delete (assoc var hide-ifdef-env
) hide-ifdef-env
)))
1598 (defun hide-ifdef-undef (start end
)
1599 "Undefine a VAR so that #ifdef VAR would not be included."
1602 (or (and mark-active
1603 (buffer-substring-no-properties start end
))
1604 (read-string "Undefine what? " (current-word))))
1607 (if (zerop (hif-defined sym
))
1608 (message "`%s' not defined, no need to undefine it" symstr
)
1609 (hif-undefine-symbol sym
)
1610 (if hide-ifdef-hiding
(hide-ifdefs))
1611 (message "`%S' undefined" sym
))))
1613 (defun hide-ifdefs (&optional nomsg
)
1614 "Hide the contents of some #ifdefs.
1615 Assume that defined symbols have been added to `hide-ifdef-env'.
1616 The text hidden is the text that would not be included by the C
1617 preprocessor if it were given the file with those symbols defined.
1619 Turn off hiding by calling `show-ifdefs'."
1622 (message "Hiding...")
1623 (setq hif-outside-read-only buffer-read-only
)
1624 (unless hide-ifdef-mode
(hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
1625 (if hide-ifdef-hiding
1626 (show-ifdefs)) ; Otherwise, deep confusion.
1627 (setq hide-ifdef-hiding t
)
1629 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
))
1631 (message "Hiding done")))
1634 (defun show-ifdefs ()
1635 "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
1637 (setq buffer-read-only hif-outside-read-only
)
1639 (setq hide-ifdef-hiding nil
))
1642 (defun hif-find-ifdef-block ()
1643 "Utility to hide and show ifdef block.
1644 Return as (TOP . BOTTOM) the extent of ifdef block."
1646 (cons (save-excursion
1648 (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
1651 (hif-ifdef-to-endif)
1652 (setq max-bottom
(1- (point)))))
1655 (unless (hif-looking-at-endif)
1656 (hif-find-next-relevant))
1657 (while (hif-looking-at-ifX)
1658 (hif-ifdef-to-endif)
1659 (hif-find-next-relevant))
1660 (min max-bottom
(1- (point)))))))
1663 (defun hide-ifdef-block ()
1664 "Hide the ifdef block (true or false part) enclosing or before the cursor."
1666 (unless hide-ifdef-mode
(hide-ifdef-mode 1))
1667 (let ((top-bottom (hif-find-ifdef-block)))
1668 (hide-ifdef-region (car top-bottom
) (cdr top-bottom
))
1669 (when hide-ifdef-lines
1670 (hif-hide-line (car top-bottom
))
1671 (hif-hide-line (1+ (cdr top-bottom
))))
1672 (setq hide-ifdef-hiding t
))
1673 (setq buffer-read-only
(or hide-ifdef-read-only hif-outside-read-only
)))
1675 (defun show-ifdef-block ()
1676 "Show the ifdef block (true or false part) enclosing or before the cursor."
1678 (let ((top-bottom (hif-find-ifdef-block)))
1679 (if hide-ifdef-lines
1680 (hif-show-ifdef-region
1682 (goto-char (car top-bottom
)) (line-beginning-position))
1684 (goto-char (1+ (cdr top-bottom
)))
1685 (hif-end-of-line) (point)))
1686 (hif-show-ifdef-region (1- (car top-bottom
)) (cdr top-bottom
)))))
1689 ;;; definition alist support
1691 (defvar hide-ifdef-define-alist nil
1692 "A global assoc list of pre-defined symbol lists.")
1694 (defun hif-compress-define-list (env)
1695 "Compress the define list ENV into a list of defined symbols only."
1696 (let ((new-defs nil
))
1697 (dolist (def env new-defs
)
1698 (if (hif-lookup (car def
)) (push (car def
) new-defs
)))))
1700 (defun hide-ifdef-set-define-alist (name)
1701 "Set the association for NAME to `hide-ifdef-env'."
1702 (interactive "SSet define list: ")
1703 (push (cons name
(hif-compress-define-list hide-ifdef-env
))
1704 hide-ifdef-define-alist
))
1706 (defun hide-ifdef-use-define-alist (name)
1707 "Set `hide-ifdef-env' to the define list specified by NAME."
1709 (list (completing-read "Use define list: "
1710 (mapcar (lambda (x) (symbol-name (car x
)))
1711 hide-ifdef-define-alist
)
1713 (if (stringp name
) (setq name
(intern name
)))
1714 (let ((define-list (assoc name hide-ifdef-define-alist
)))
1716 (setq hide-ifdef-env
1717 (mapcar (lambda (arg) (cons arg t
))
1719 (error "No define list for %s" name
))
1720 (if hide-ifdef-hiding
(hide-ifdefs))))
1724 ;;; hideif.el ends here