]> code.delx.au - gnu-emacs/blob - lisp/progmodes/hideif.el
71646d312c40d30b0bf96dce149ce615ae111f31
[gnu-emacs] / lisp / progmodes / hideif.el
1 ;;; hideif.el --- hides selected code within ifdef -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 1988, 1994, 2001-2015 Free Software Foundation, Inc.
4
5 ;; Author: Brian Marick
6 ;; Daniel LaLiberte <liberte@holonexus.org>
7 ;; Maintainer: Luke Lee <luke.yx.lee@gmail.com>
8 ;; Keywords: c, outlines
9
10 ;; This file is part of GNU Emacs.
11
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.
16
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.
21
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/>.
24
25 ;;; Commentary:
26
27 ;; To initialize, toggle the hide-ifdef minor mode with
28 ;;
29 ;; M-x hide-ifdef-mode
30 ;;
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
34 ;;
35 ;; M-x hide-ifdefs or C-c @ h
36 ;;
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
40 ;; parsing.
41 ;;
42 ;; The hidden code is marked by ellipses (...). Be
43 ;; cautious when editing near ellipses, since the hidden text is
44 ;; still in the buffer, and you can move the point into it and modify
45 ;; text unawares.
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).
49 ;;
50 ;; You can undo the effect of hide-ifdefs by typing
51 ;;
52 ;; M-x show-ifdefs or C-c @ s
53 ;;
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.
56 ;;
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.
62 ;;
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
65 ;; following example:
66 ;;
67 ;; (add-hook 'hide-ifdef-mode-hook
68 ;; (lambda ()
69 ;; (unless hide-ifdef-define-alist
70 ;; (setq hide-ifdef-define-alist
71 ;; '((list1 ONE TWO)
72 ;; (list2 TWO THREE))))
73 ;; (hide-ifdef-use-define-alist 'list2))) ; use list2 by default
74 ;;
75 ;; You can call hide-ifdef-use-define-alist (C-c @ U) at any time to specify
76 ;; another list to use.
77 ;;
78 ;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
79 ;; set hide-ifdef-initially to non-nil.
80 ;;
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
86 ;; better.
87 ;;
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.
90 ;;
91 ;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
92 ;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
93 ;;
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).
97 ;;
98 ;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
99 ;; Extensively modified by Daniel LaLiberte (while at Gould).
100 ;;
101 ;; Extensively modified by Luke Lee in 2013 to support complete C expression
102 ;; evaluation and argumented macro expansion.
103
104 ;;; Code:
105
106 (require 'cc-mode)
107 (require 'cl-lib)
108
109 (defgroup hide-ifdef nil
110 "Hide selected code within `ifdef'."
111 :group 'c)
112
113 (defcustom hide-ifdef-initially nil
114 "Non-nil means call `hide-ifdefs' when Hide-Ifdef mode is first activated."
115 :type 'boolean
116 :group 'hide-ifdef)
117
118 (defcustom hide-ifdef-read-only nil
119 "Set to non-nil if you want buffer to be read-only while hiding text."
120 :type 'boolean
121 :group 'hide-ifdef)
122
123 (defcustom hide-ifdef-lines nil
124 "Non-nil means hide the #ifX, #else, and #endif lines."
125 :type 'boolean
126 :group 'hide-ifdef)
127
128 (defcustom hide-ifdef-shadow nil
129 "Non-nil means shadow text instead of hiding it."
130 :type 'boolean
131 :group 'hide-ifdef
132 :version "23.1")
133
134 (defface hide-ifdef-shadow '((t (:inherit shadow)))
135 "Face for shadowing ifdef blocks."
136 :group 'hide-ifdef
137 :version "23.1")
138
139 (defcustom hide-ifdef-exclude-define-regexp nil
140 "Ignore #define names if those names match this exclusion pattern."
141 :type 'string
142 :version "25.1")
143
144 (defcustom hide-ifdef-expand-reinclusion-protection t
145 "Non-nil means don't hide an entire header file enclosed by #ifndef...#endif.
146 Most C/C++ headers are usually wrapped with ifdefs to prevent re-inclusion:
147
148 ----- beginning of file -----
149 #ifndef _XXX_HEADER_FILE_INCLUDED_
150 #define _XXX_HEADER_FILE_INCLUDED_
151 xxx
152 xxx
153 xxx...
154 #endif
155 ----- end of file -----
156
157 The first time we visit such a file, _XXX_HEADER_FILE_INCLUDED_ is
158 undefined, and so nothing is hidden. The next time we visit it, everything will
159 be hidden.
160
161 This behavior is generally undesirable. If this option is non-nil, the outermost
162 #if is always visible."
163 :type 'boolean
164 :version "25.1")
165
166 (defcustom hide-ifdef-header-regexp
167 "\\.h\\(h\\|xx\\|pp\\|\\+\\+\\)?\\'"
168 "C/C++ header file name patterns to determine if current buffer is a header.
169 Effective only if `hide-ifdef-expand-reinclusion-protection' is t."
170 :type 'string
171 :group 'hide-ifdef
172 :version "25.1")
173
174 (defvar hide-ifdef-mode-submap
175 ;; Set up the submap that goes after the prefix key.
176 (let ((map (make-sparse-keymap)))
177 (define-key map "d" 'hide-ifdef-define)
178 (define-key map "u" 'hide-ifdef-undef)
179 (define-key map "D" 'hide-ifdef-set-define-alist)
180 (define-key map "U" 'hide-ifdef-use-define-alist)
181
182 (define-key map "h" 'hide-ifdefs)
183 (define-key map "s" 'show-ifdefs)
184 (define-key map "\C-d" 'hide-ifdef-block)
185 (define-key map "\C-s" 'show-ifdef-block)
186 (define-key map "e" 'hif-evaluate-macro)
187 (define-key map "C" 'hif-clear-all-ifdef-defined)
188
189 (define-key map "\C-q" 'hide-ifdef-toggle-read-only)
190 (define-key map "\C-w" 'hide-ifdef-toggle-shadowing)
191 (substitute-key-definition
192 'read-only-mode 'hide-ifdef-toggle-outside-read-only map)
193 ;; `toggle-read-only' is obsoleted by `read-only-mode'.
194 (substitute-key-definition
195 'toggle-read-only 'hide-ifdef-toggle-outside-read-only map)
196 map)
197 "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'.")
198
199 (defconst hide-ifdef-mode-prefix-key "\C-c@"
200 "Prefix key for all Hide-Ifdef mode commands.")
201
202 (defvar hide-ifdef-mode-map
203 ;; Set up the mode's main map, which leads via the prefix key to the submap.
204 (let ((map (make-sparse-keymap)))
205 (define-key map hide-ifdef-mode-prefix-key hide-ifdef-mode-submap)
206 map)
207 "Keymap used with `hide-ifdef-mode'.")
208
209 (easy-menu-define hide-ifdef-mode-menu hide-ifdef-mode-map
210 "Menu for `hide-ifdef-mode'."
211 '("Hide-Ifdef"
212 ["Hide some ifdefs" hide-ifdefs
213 :help "Hide the contents of some #ifdefs"]
214 ["Show all ifdefs" show-ifdefs
215 :help "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs"]
216 ["Hide ifdef block" hide-ifdef-block
217 :help "Hide the ifdef block (true or false part) enclosing or before the cursor"]
218 ["Show ifdef block" show-ifdef-block
219 :help "Show the ifdef block (true or false part) enclosing or before the cursor"]
220 ["Define a variable..." hide-ifdef-define
221 :help "Define a VAR so that #ifdef VAR would be included"]
222 ["Undefine a variable..." hide-ifdef-undef
223 :help "Undefine a VAR so that #ifdef VAR would not be included"]
224 ["Define an alist..." hide-ifdef-set-define-alist
225 :help "Set the association for NAME to `hide-ifdef-env'"]
226 ["Use an alist..." hide-ifdef-use-define-alist
227 :help "Set `hide-ifdef-env' to the define list specified by NAME"]
228 ["Toggle read only" hide-ifdef-toggle-read-only
229 :style toggle :selected hide-ifdef-read-only
230 :help "Buffer should be read-only while hiding text"]
231 ["Toggle shadowing" hide-ifdef-toggle-shadowing
232 :style toggle :selected hide-ifdef-shadow
233 :help "Text should be shadowed instead of hidden"]))
234
235 (defvar hide-ifdef-hiding nil
236 "Non-nil when text may be hidden.")
237
238 (or (assq 'hide-ifdef-hiding minor-mode-alist)
239 (setq minor-mode-alist
240 (cons '(hide-ifdef-hiding " Hiding")
241 minor-mode-alist)))
242
243 ;; Fix c-mode syntax table so we can recognize whole symbols.
244 (defvar hide-ifdef-syntax-table
245 (let ((st (copy-syntax-table c-mode-syntax-table)))
246 (modify-syntax-entry ?_ "w" st)
247 (modify-syntax-entry ?& "." st)
248 (modify-syntax-entry ?\| "." st)
249 st)
250 "Syntax table used for tokenizing #if expressions.")
251
252 (defvar hide-ifdef-env nil
253 "An alist of defined symbols and their values.")
254
255 (defvar hide-ifdef-env-backup nil
256 "This variable is a backup of the previously cleared `hide-ifdef-env'.
257 This backup prevents any accidental clearance of `hide-fidef-env' by
258 `hif-clear-all-ifdef-defined'.")
259
260 (defvar hif-outside-read-only nil
261 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
262
263 ;;;###autoload
264 (define-minor-mode hide-ifdef-mode
265 "Toggle features to hide/show #ifdef blocks (Hide-Ifdef mode).
266 With a prefix argument ARG, enable Hide-Ifdef mode if ARG is
267 positive, and disable it otherwise. If called from Lisp, enable
268 the mode if ARG is omitted or nil.
269
270 Hide-Ifdef mode is a buffer-local minor mode for use with C and
271 C-like major modes. When enabled, code within #ifdef constructs
272 that the C preprocessor would eliminate may be hidden from view.
273 Several variables affect how the hiding is done:
274
275 `hide-ifdef-env'
276 An association list of defined and undefined symbols for the
277 current project. Initially, the global value of `hide-ifdef-env'
278 is used. This variable was a buffer-local variable, which limits
279 hideif to parse only one C/C++ file at a time. We've extended
280 hideif to support parsing a C/C++ project containing multiple C/C++
281 source files opened simultaneously in different buffers. Therefore
282 `hide-ifdef-env' can no longer be buffer local but must be global.
283
284 `hide-ifdef-define-alist'
285 An association list of defined symbol lists.
286 Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
287 and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
288 from one of the lists in `hide-ifdef-define-alist'.
289
290 `hide-ifdef-lines'
291 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
292 #endif lines when hiding.
293
294 `hide-ifdef-initially'
295 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
296 is activated.
297
298 `hide-ifdef-read-only'
299 Set to non-nil if you want to make buffers read only while hiding.
300 After `show-ifdefs', read-only status is restored to previous value.
301
302 \\{hide-ifdef-mode-map}"
303 :group 'hide-ifdef :lighter " Ifdef"
304 (if hide-ifdef-mode
305 (progn
306 ;; inherit global values
307
308 ;; `hide-ifdef-env' is now a global variable.
309 ;; We can still simulate the behavior of older hideif versions (i.e.
310 ;; `hide-ifdef-env' being buffer local) by clearing this variable
311 ;; (C-c @ C) everytime before hiding current buffer.
312 ;; (set (make-local-variable 'hide-ifdef-env)
313 ;; (default-value 'hide-ifdef-env))
314 (set 'hide-ifdef-env (default-value 'hide-ifdef-env))
315 ;; Some C/C++ headers might have other ways to prevent reinclusion and
316 ;; thus would like `hide-ifdef-expand-reinclusion-protection' to be nil.
317 (set (make-local-variable 'hide-ifdef-expand-reinclusion-protection)
318 (default-value 'hide-ifdef-expand-reinclusion-protection))
319 (set (make-local-variable 'hide-ifdef-hiding)
320 (default-value 'hide-ifdef-hiding))
321 (set (make-local-variable 'hif-outside-read-only) buffer-read-only)
322 (set (make-local-variable 'line-move-ignore-invisible) t)
323 (add-hook 'change-major-mode-hook
324 (lambda () (hide-ifdef-mode -1)) nil t)
325
326 (add-to-invisibility-spec '(hide-ifdef . t))
327
328 (if hide-ifdef-initially
329 (hide-ifdefs)
330 (show-ifdefs)))
331 ;; else end hide-ifdef-mode
332 (kill-local-variable 'line-move-ignore-invisible)
333 (remove-from-invisibility-spec '(hide-ifdef . t))
334 (when hide-ifdef-hiding
335 (show-ifdefs))))
336
337 (defun hif-clear-all-ifdef-defined ()
338 "Clears all symbols defined in `hide-ifdef-env'.
339 It will backup this variable to `hide-ifdef-env-backup' before clearing to
340 prevent accidental clearance."
341 (interactive)
342 (when (y-or-n-p "Clear all #defined symbols? ")
343 (setq hide-ifdef-env-backup hide-ifdef-env)
344 (setq hide-ifdef-env nil)))
345
346 (defun hif-show-all ()
347 "Show all of the text in the current buffer."
348 (interactive)
349 (hif-show-ifdef-region (point-min) (point-max)))
350
351 ;; By putting this on after-revert-hook, we arrange that it only
352 ;; does anything when revert-buffer avoids turning off the mode.
353 ;; (That can happen in VC.)
354 (defun hif-after-revert-function ()
355 (and hide-ifdef-mode hide-ifdef-hiding
356 (hide-ifdefs t)))
357 (add-hook 'after-revert-hook 'hif-after-revert-function)
358
359 (defun hif-end-of-line ()
360 (end-of-line)
361 (while (= (logand 1 (skip-chars-backward "\\\\")) 1)
362 (end-of-line 2)))
363
364 (defun hif-merge-ifdef-region (start end)
365 "This function merges nearby ifdef regions to form a bigger overlay.
366 The region is defined by START and END. This will decrease the number of
367 overlays created."
368 ;; Generally there is no need to call itself recursively since there should
369 ;; originally exists no un-merged regions; however, if a part of the file is
370 ;; hidden with `hide-ifdef-lines' equals to nil while another part with 't,
371 ;; this case happens.
372 ;; TODO: Should we merge? or just create a container overlay? -- this can
373 ;; prevent `hideif-show-ifdef' expanding too many hidden contents since there
374 ;; is only a big overlay exists there without any smaller overlays.
375 (save-restriction
376 (widen) ; Otherwise `point-min' and `point-max' will be restricted and thus
377 ; fail to find neighbor overlays
378 (let ((begovrs (overlays-in
379 (max (- start 2) (point-min))
380 (max (- start 1) (point-min))))
381 (endovrs (overlays-in
382 (min (+ end 1) (point-max))
383 (min (+ end 2) (point-max))))
384 (ob nil)
385 (oe nil)
386 b e)
387 ;; Merge overlays before START
388 (dolist (o begovrs)
389 (when (overlay-get o 'hide-ifdef)
390 (setq b (min start (overlay-start o))
391 e (max end (overlay-end o)))
392 (move-overlay o b e)
393 (hif-merge-ifdef-region b e)
394 (setq ob o)))
395 ;; Merge overlays after END
396 (dolist (o endovrs)
397 (when (overlay-get o 'hide-ifdef)
398 (setq b (min start (overlay-start o))
399 e (max end (overlay-end o)))
400 (move-overlay o b e)
401 (hif-merge-ifdef-region b e)
402 (setf oe o)))
403 ;; If both START and END merging happens, merge into bigger one
404 (when (and ob oe)
405 (let ((b (min (overlay-start ob) (overlay-start oe)))
406 (e (max (overlay-end ob) (overlay-end oe))))
407 (delete-overlay oe)
408 (move-overlay ob b e)
409 (hif-merge-ifdef-region b e)))
410 (or ob oe))))
411
412 (defun hide-ifdef-region-internal (start end)
413 (unless (hif-merge-ifdef-region start end)
414 (let ((o (make-overlay start end)))
415 (overlay-put o 'hide-ifdef t)
416 (if hide-ifdef-shadow
417 (overlay-put o 'face 'hide-ifdef-shadow)
418 (overlay-put o 'invisible 'hide-ifdef)))))
419
420 (defun hide-ifdef-region (start end)
421 "START is the start of a #if, #elif, or #else form. END is the ending part.
422 Everything including these lines is made invisible."
423 (save-excursion
424 (goto-char start) (hif-end-of-line) (setq start (point))
425 (goto-char end) (hif-end-of-line) (setq end (point))
426 (hide-ifdef-region-internal start end)))
427
428 (defun hif-show-ifdef-region (start end)
429 "Everything between START and END is made visible."
430 (let ((onum (length (overlays-in start end))))
431 (remove-overlays start end 'hide-ifdef t)
432 (/= onum (length (overlays-in start end)))))
433
434
435 ;;===%%SF%% evaluation (Start) ===
436
437 ;; It is not useful to set this to anything but `eval'.
438 ;; In fact, the variable might as well be eliminated.
439 (defvar hide-ifdef-evaluator 'eval
440 "The function to use to evaluate a form.
441 The evaluator is given a canonical form and returns t if text under
442 that form should be displayed.")
443
444 (defvar hif-undefined-symbol nil
445 "...is by default considered to be false.")
446
447
448 (defun hif-set-var (var value)
449 "Prepend (VAR VALUE) pair to `hide-ifdef-env'."
450 (setq hide-ifdef-env (cons (cons var value) hide-ifdef-env)))
451
452 (declare-function semantic-c-hideif-lookup "semantic/bovine/c" (var))
453 (declare-function semantic-c-hideif-defined "semantic/bovine/c" (var))
454
455 (defun hif-lookup (var)
456 (or (when (bound-and-true-p semantic-c-takeover-hideif)
457 (semantic-c-hideif-lookup var))
458 (let ((val (assoc var hide-ifdef-env)))
459 (if val
460 (cdr val)
461 hif-undefined-symbol))))
462
463 (defun hif-defined (var)
464 (cond
465 ((bound-and-true-p semantic-c-takeover-hideif)
466 (semantic-c-hideif-defined var))
467 ((assoc var hide-ifdef-env) 1)
468 (t 0)))
469
470 ;;===%%SF%% evaluation (End) ===
471
472
473
474 ;;===%%SF%% parsing (Start) ===
475 ;;; The code that understands what ifs and ifdef in files look like.
476
477 (defconst hif-cpp-prefix "\\(^\\|\r\\)[ \t]*#[ \t]*")
478 (defconst hif-ifxdef-regexp (concat hif-cpp-prefix "if\\(n\\)?def"))
479 (defconst hif-ifndef-regexp (concat hif-cpp-prefix "ifndef"))
480 (defconst hif-ifx-regexp (concat hif-cpp-prefix "if\\(n?def\\)?[ \t]+"))
481 (defconst hif-elif-regexp (concat hif-cpp-prefix "elif"))
482 (defconst hif-else-regexp (concat hif-cpp-prefix "else"))
483 (defconst hif-endif-regexp (concat hif-cpp-prefix "endif"))
484 (defconst hif-ifx-else-endif-regexp
485 (concat hif-ifx-regexp "\\|" hif-elif-regexp "\\|" hif-else-regexp "\\|"
486 hif-endif-regexp))
487 (defconst hif-macro-expr-prefix-regexp
488 (concat hif-cpp-prefix "\\(if\\(n?def\\)?\\|elif\\|define\\)[ \t]+"))
489
490 (defconst hif-white-regexp "[ \t]*")
491 (defconst hif-define-regexp (concat hif-cpp-prefix "\\(define\\|undef\\)"))
492 (defconst hif-id-regexp (concat "[[:alpha:]_][[:alnum:]_]*"))
493 (defconst hif-macroref-regexp
494 (concat hif-white-regexp "\\(" hif-id-regexp "\\)" hif-white-regexp
495 "\\("
496 "(" hif-white-regexp
497 "\\(" hif-id-regexp "\\)?" hif-white-regexp
498 "\\(" "," hif-white-regexp hif-id-regexp hif-white-regexp "\\)*"
499 "\\(\\.\\.\\.\\)?" hif-white-regexp
500 ")"
501 "\\)?" ))
502
503 ;; Store the current token and the whole token list during parsing.
504 ;; Bound dynamically.
505 (defvar hif-token)
506 (defvar hif-token-list)
507
508 (defconst hif-token-alist
509 '(("||" . hif-or)
510 ("&&" . hif-and)
511 ("|" . hif-logior)
512 ("^" . hif-logxor)
513 ("&" . hif-logand)
514 ("<<" . hif-shiftleft)
515 (">>" . hif-shiftright)
516 ("==" . hif-equal)
517 ;; Note: we include tokens like `=' which aren't supported by CPP's
518 ;; expression syntax, because they are still relevant for the tokenizer,
519 ;; especially in conjunction with ##.
520 ("=" . hif-assign)
521 ("!=" . hif-notequal)
522 ("##" . hif-token-concat)
523 ("!" . hif-not)
524 ("~" . hif-lognot)
525 ("(" . hif-lparen)
526 (")" . hif-rparen)
527 (">" . hif-greater)
528 ("<" . hif-less)
529 (">=" . hif-greater-equal)
530 ("<=" . hif-less-equal)
531 ("+" . hif-plus)
532 ("-" . hif-minus)
533 ("*" . hif-multiply)
534 ("/" . hif-divide)
535 ("%" . hif-modulo)
536 ("?" . hif-conditional)
537 (":" . hif-colon)
538 ("," . hif-comma)
539 ("#" . hif-stringify)
540 ("..." . hif-etc)))
541
542 (defconst hif-valid-token-list (mapcar 'cdr hif-token-alist))
543
544 (defconst hif-token-regexp
545 (concat (regexp-opt (mapcar 'car hif-token-alist))
546 "\\|0x[0-9a-fA-F]+\\.?[0-9a-fA-F]*"
547 "\\|[0-9]+\\.?[0-9]*" ;; decimal/octal
548 "\\|\\w+"))
549
550 (defconst hif-string-literal-regexp "\\(\"\\(?:[^\"\\]\\|\\\\.\\)*\"\\)")
551
552 (defun hif-string-to-number (string &optional base)
553 "Like `string-to-number', but it understands non-decimal floats."
554 (if (or (not base) (= base 10))
555 (string-to-number string base)
556 (let* ((parts (split-string string "\\." t "[ \t]+"))
557 (frac (cadr parts))
558 (fraclen (length frac))
559 (quot (expt (if (zerop fraclen)
560 base
561 (* base 1.0)) fraclen)))
562 (/ (string-to-number (concat (car parts) frac) base) quot))))
563
564 ;; The dynamic binding variable `hif-simple-token-only' is shared only by
565 ;; `hif-tokenize' and `hif-find-define'. The purpose is to prevent `hif-tokenize'
566 ;; from returning one more value to indicate a simple token is scanned. This help
567 ;; speeding up macro evaluation on those very simple cases like integers or
568 ;; literals.
569 ;; Check the long comments before `hif-find-define' for more details. [lukelee]
570 (defvar hif-simple-token-only)
571
572 (defun hif-tokenize (start end)
573 "Separate string between START and END into a list of tokens."
574 (let ((token-list nil))
575 (setq hif-simple-token-only t)
576 (with-syntax-table hide-ifdef-syntax-table
577 (save-excursion
578 (goto-char start)
579 (while (progn (forward-comment (point-max)) (< (point) end))
580 ;; (message "expr-start = %d" expr-start) (sit-for 1)
581 (cond
582 ((looking-at "\\\\\n")
583 (forward-char 2))
584
585 ((looking-at hif-string-literal-regexp)
586 (push (substring-no-properties (match-string 1)) token-list)
587 (goto-char (match-end 0)))
588
589 ((looking-at hif-token-regexp)
590 (let ((token (buffer-substring-no-properties
591 (point) (match-end 0))))
592 (goto-char (match-end 0))
593 ;; (message "token: %s" token) (sit-for 1)
594 (push
595 (or (cdr (assoc token hif-token-alist))
596 (if (string-equal token "defined") 'hif-defined)
597 ;; TODO:
598 ;; 1. postfix 'l', 'll', 'ul' and 'ull'
599 ;; 2. floating number formats (like 1.23e4)
600 ;; 3. 098 is interpreted as octal conversion error
601 (if (string-match "0x\\([0-9a-fA-F]+\\.?[0-9a-fA-F]*\\)"
602 token)
603 (hif-string-to-number (match-string 1 token) 16)) ;; hex
604 (if (string-match "\\`0[0-9]+\\(\\.[0-9]+\\)?\\'" token)
605 (hif-string-to-number token 8)) ;; octal
606 (if (string-match "\\`[1-9][0-9]*\\(\\.[0-9]+\\)?\\'"
607 token)
608 (string-to-number token)) ;; decimal
609 (prog1 (intern token)
610 (setq hif-simple-token-only nil)))
611 token-list)))
612
613 ((looking-at "\r") ; Sometimes MS-Windows user will leave CR in
614 (forward-char 1)) ; the source code. Let's not get stuck here.
615 (t (error "Bad #if expression: %s" (buffer-string)))))))
616
617 (nreverse token-list)))
618
619 ;;------------------------------------------------------------------------
620 ;; Translate C preprocessor #if expressions using recursive descent.
621 ;; This parser was limited to the operators &&, ||, !, and "defined".
622 ;; Added ==, !=, +, and -. Gary Oberbrunner, garyo@avs.com, 8/9/94
623 ;;
624 ;; Implement the C language operator precedence table. Add all those
625 ;; missing operators that could be used in macros. Luke Lee 2013-09-04
626
627 ;; | Operator Type | Operator | Associativity |
628 ;; +----------------------+-----------------------------+---------------+
629 ;; | Primary Expression | () [] . -> expr++ expr-- | left-to-right |
630 ;; | Unary Operators | * & + - ! ~ ++expr --expr | right-to-left |
631 ;; | | (typecast) sizeof | |
632 ;; | Binary Operators | * / % | left-to-right |
633 ;; | | + - | |
634 ;; | | >> << | |
635 ;; | | < > <= >= | |
636 ;; | | == != | |
637 ;; | | & | |
638 ;; | | ^ | |
639 ;; | | | | |
640 ;; | | && | |
641 ;; | | || | |
642 ;; | Ternary Operator | ?: | right-to-left |
643 ;; x| Assignment Operators | = += -= *= /= %= >>= <<= &= | right-to-left |
644 ;; | | ^= = | |
645 ;; | Comma | , | left-to-right |
646
647 (defsubst hif-nexttoken ()
648 "Pop the next token from token-list into the let variable `hif-token'."
649 (setq hif-token (pop hif-token-list)))
650
651 (defsubst hif-if-valid-identifier-p (id)
652 (not (or (numberp id)
653 (stringp id))))
654
655 (defun hif-define-operator (tokens)
656 "\"Upgrade\" hif-define XXX to `(hif-define XXX)' so it won't be substituted."
657 (let ((result nil)
658 (tok nil))
659 (while (setq tok (pop tokens))
660 (push
661 (if (eq tok 'hif-defined)
662 (progn
663 (setq tok (cadr tokens))
664 (if (eq (car tokens) 'hif-lparen)
665 (if (and (hif-if-valid-identifier-p tok)
666 (eq (nth 2 tokens) 'hif-rparen))
667 (setq tokens (cl-cdddr tokens))
668 (error "#define followed by non-identifier: %S" tok))
669 (setq tok (car tokens)
670 tokens (cdr tokens))
671 (unless (hif-if-valid-identifier-p tok)
672 (error "#define followed by non-identifier: %S" tok)))
673 (list 'hif-defined 'hif-lparen tok 'hif-rparen))
674 tok)
675 result))
676 (nreverse result)))
677
678 (defun hif-flatten (l)
679 "Flatten a tree."
680 (apply #'nconc
681 (mapcar (lambda (x) (if (listp x)
682 (hif-flatten x)
683 (list x))) l)))
684
685 (defun hif-expand-token-list (tokens &optional macroname expand_list)
686 "Perform expansion on TOKENS till everything expanded.
687 Self-reference (directly or indirectly) tokens are not expanded.
688 EXPAND_LIST is the list of macro names currently being expanded, used for
689 detecting self-reference."
690 (catch 'self-referencing
691 (let ((expanded nil)
692 (remains (hif-define-operator
693 (hif-token-concatenation
694 (hif-token-stringification tokens))))
695 tok rep)
696 (if macroname
697 (setq expand_list (cons macroname expand_list)))
698 ;; Expanding all tokens till list exhausted
699 (while (setq tok (pop remains))
700 (if (memq tok expand_list)
701 ;; For self-referencing tokens, don't expand it
702 (throw 'self-referencing tokens))
703 (push
704 (cond
705 ((or (memq tok hif-valid-token-list)
706 (numberp tok)
707 (stringp tok))
708 tok)
709
710 ((setq rep (hif-lookup tok))
711 (if (and (listp rep)
712 (eq (car rep) 'hif-define-macro)) ; A defined macro
713 ;; Recursively expand it
714 (if (cadr rep) ; Argument list is not nil
715 (if (not (eq (car remains) 'hif-lparen))
716 ;; No argument, no invocation
717 tok
718 ;; Argumented macro, get arguments and invoke it.
719 ;; Dynamically bind hif-token-list and hif-token
720 ;; for hif-macro-supply-arguments
721 (let* ((hif-token-list (cdr remains))
722 (hif-token nil)
723 (parmlist (mapcar #'hif-expand-token-list
724 (hif-get-argument-list)))
725 (result
726 (hif-expand-token-list
727 (hif-macro-supply-arguments tok parmlist)
728 tok expand_list)))
729 (setq remains (cons hif-token hif-token-list))
730 result))
731 ;; Argument list is nil, direct expansion
732 (setq rep (hif-expand-token-list
733 (nth 2 rep) ; Macro's token list
734 tok expand_list))
735 ;; Replace all remaining references immediately
736 (setq remains (cl-substitute tok rep remains))
737 rep)
738 ;; Lookup tok returns an atom
739 rep))
740
741 ;;[2013-10-22 16:06:12 +0800] Must keep the token, removing
742 ;; this token might results in an incomplete expression that
743 ;; cannot be parsed further.
744 ;;((= 1 (hif-defined tok)) ; defined (hif-defined tok)=1,
745 ;; ;;but empty (hif-lookup tok)=nil, thus remove this token
746 ;; (setq remains (delete tok remains))
747 ;; nil)
748
749 (t ; Usual IDs
750 tok))
751
752 expanded))
753
754 (hif-flatten (nreverse expanded)))))
755
756 (defun hif-parse-exp (token-list &optional macroname)
757 "Parse the TOKEN-LIST.
758 Return translated list in prefix form. MACRONAME is applied when invoking
759 macros to prevent self-reference."
760 (let ((hif-token-list (hif-expand-token-list token-list macroname)))
761 (hif-nexttoken)
762 (prog1
763 (and hif-token
764 (hif-exprlist))
765 (if hif-token ; is there still a token?
766 (error "Error: unexpected token: %s" hif-token)))))
767
768 (defun hif-exprlist ()
769 "Parse an exprlist: expr { `,' expr}."
770 (let ((result (hif-expr)))
771 (if (eq hif-token 'hif-comma)
772 (let ((temp (list result)))
773 (while
774 (progn
775 (hif-nexttoken)
776 (push (hif-expr) temp)
777 (eq hif-token 'hif-comma)))
778 (cons 'hif-comma (nreverse temp)))
779 result)))
780
781 (defun hif-expr ()
782 "Parse an expression as found in #if.
783 expr : or-expr | or-expr `?' expr `:' expr."
784 (let ((result (hif-or-expr))
785 middle)
786 (while (eq hif-token 'hif-conditional)
787 (hif-nexttoken)
788 (setq middle (hif-expr))
789 (if (eq hif-token 'hif-colon)
790 (progn
791 (hif-nexttoken)
792 (setq result (list 'hif-conditional result middle (hif-expr))))
793 (error "Error: unexpected token: %s" hif-token)))
794 result))
795
796 (defun hif-or-expr ()
797 "Parse an or-expr : and-expr | or-expr `||' and-expr."
798 (let ((result (hif-and-expr)))
799 (while (eq hif-token 'hif-or)
800 (hif-nexttoken)
801 (setq result (list 'hif-or result (hif-and-expr))))
802 result))
803
804 (defun hif-and-expr ()
805 "Parse an and-expr : logior-expr | and-expr `&&' logior-expr."
806 (let ((result (hif-logior-expr)))
807 (while (eq hif-token 'hif-and)
808 (hif-nexttoken)
809 (setq result (list 'hif-and result (hif-logior-expr))))
810 result))
811
812 (defun hif-logior-expr ()
813 "Parse a logor-expr : logxor-expr | logor-expr `|' logxor-expr."
814 (let ((result (hif-logxor-expr)))
815 (while (eq hif-token 'hif-logior)
816 (hif-nexttoken)
817 (setq result (list 'hif-logior result (hif-logxor-expr))))
818 result))
819
820 (defun hif-logxor-expr ()
821 "Parse a logxor-expr : logand-expr | logxor-expr `^' logand-expr."
822 (let ((result (hif-logand-expr)))
823 (while (eq hif-token 'hif-logxor)
824 (hif-nexttoken)
825 (setq result (list 'hif-logxor result (hif-logand-expr))))
826 result))
827
828 (defun hif-logand-expr ()
829 "Parse a logand-expr : eq-expr | logand-expr `&' eq-expr."
830 (let ((result (hif-eq-expr)))
831 (while (eq hif-token 'hif-logand)
832 (hif-nexttoken)
833 (setq result (list 'hif-logand result (hif-eq-expr))))
834 result))
835
836 (defun hif-eq-expr ()
837 "Parse an eq-expr : comp | eq-expr `=='|`!=' comp."
838 (let ((result (hif-comp-expr))
839 (eq-token nil))
840 (while (memq hif-token '(hif-equal hif-notequal))
841 (setq eq-token hif-token)
842 (hif-nexttoken)
843 (setq result (list eq-token result (hif-comp-expr))))
844 result))
845
846 (defun hif-comp-expr ()
847 "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
848 (let ((result (hif-logshift-expr))
849 (comp-token nil))
850 (while (memq hif-token '(hif-greater hif-less hif-greater-equal
851 hif-less-equal))
852 (setq comp-token hif-token)
853 (hif-nexttoken)
854 (setq result (list comp-token result (hif-logshift-expr))))
855 result))
856
857 (defun hif-logshift-expr ()
858 "Parse a logshift : math | logshift `<<'|`>>' math."
859 (let ((result (hif-math))
860 (shift-token nil))
861 (while (memq hif-token '(hif-shiftleft hif-shiftright))
862 (setq shift-token hif-token)
863 (hif-nexttoken)
864 (setq result (list shift-token result (hif-math))))
865 result))
866
867 (defun hif-math ()
868 "Parse an expression with + or -.
869 math : muldiv | math `+'|`-' muldiv."
870 (let ((result (hif-muldiv-expr))
871 (math-op nil))
872 (while (memq hif-token '(hif-plus hif-minus))
873 (setq math-op hif-token)
874 (hif-nexttoken)
875 (setq result (list math-op result (hif-muldiv-expr))))
876 result))
877
878 (defun hif-muldiv-expr ()
879 "Parse an expression with *,/,%.
880 muldiv : factor | muldiv `*'|`/'|`%' factor."
881 (let ((result (hif-factor))
882 (math-op nil))
883 (while (memq hif-token '(hif-multiply hif-divide hif-modulo))
884 (setq math-op hif-token)
885 (hif-nexttoken)
886 (setq result (list math-op result (hif-factor))))
887 result))
888
889 (defun hif-factor ()
890 "Parse a factor.
891 factor : `!' factor | `~' factor | `(' expr `)' | `defined(' id `)' |
892 id `(' parmlist `)' | strings | id."
893 (cond
894 ((eq hif-token 'hif-not)
895 (hif-nexttoken)
896 (list 'hif-not (hif-factor)))
897
898 ((eq hif-token 'hif-lognot)
899 (hif-nexttoken)
900 (list 'hif-lognot (hif-factor)))
901
902 ((eq hif-token 'hif-lparen)
903 (hif-nexttoken)
904 (let ((result (hif-exprlist)))
905 (if (not (eq hif-token 'hif-rparen))
906 (error "Bad token in parenthesized expression: %s" hif-token)
907 (hif-nexttoken)
908 result)))
909
910 ((eq hif-token 'hif-defined)
911 (hif-nexttoken)
912 (let ((paren (when (eq hif-token 'hif-lparen) (hif-nexttoken) t))
913 (ident hif-token))
914 (if (memq hif-token '(or and not hif-defined hif-lparen hif-rparen))
915 (error "Error: unexpected token: %s" hif-token))
916 (when paren
917 (hif-nexttoken)
918 (unless (eq hif-token 'hif-rparen)
919 (error "Error: expected \")\" after identifier")))
920 (hif-nexttoken)
921 `(hif-defined (quote ,ident))))
922
923 ((numberp hif-token)
924 (prog1 hif-token (hif-nexttoken)))
925 ((stringp hif-token)
926 (hif-string-concatenation))
927
928 ;; Unary plus/minus.
929 ((memq hif-token '(hif-minus hif-plus))
930 (list (prog1 hif-token (hif-nexttoken)) 0 (hif-factor)))
931
932 (t ; identifier
933 (let ((ident hif-token))
934 (hif-nexttoken)
935 (if (eq hif-token 'hif-lparen)
936 (hif-place-macro-invocation ident)
937 `(hif-lookup (quote ,ident)))))))
938
939 (defun hif-get-argument-list ()
940 (let ((nest 0)
941 (parmlist nil) ; A "token" list of parameters, will later be parsed
942 (parm nil))
943
944 (while (or (not (eq (hif-nexttoken) 'hif-rparen))
945 (/= nest 0))
946 (if (eq (car (last parm)) 'hif-comma)
947 (setq parm nil))
948 (cond
949 ((eq hif-token 'hif-lparen)
950 (setq nest (1+ nest)))
951 ((eq hif-token 'hif-rparen)
952 (setq nest (1- nest)))
953 ((and (eq hif-token 'hif-comma)
954 (= nest 0))
955 (push (nreverse parm) parmlist)
956 (setq parm nil)))
957 (push hif-token parm))
958
959 (push (nreverse parm) parmlist) ; Okay even if PARM is nil
960 (hif-nexttoken) ; Drop the `hif-rparen', get next token
961 (nreverse parmlist)))
962
963 (defun hif-place-macro-invocation (ident)
964 (let ((parmlist (hif-get-argument-list)))
965 `(hif-invoke (quote ,ident) (quote ,parmlist))))
966
967 (defun hif-string-concatenation ()
968 "Parse concatenated strings: string | strings string."
969 (let ((result (substring-no-properties hif-token)))
970 (while (stringp (hif-nexttoken))
971 (setq result (concat
972 (substring result 0 -1) ; remove trailing '"'
973 (substring hif-token 1)))) ; remove leading '"'
974 result))
975
976 (defun hif-define-macro (_parmlist _token-body)
977 "A marker for defined macro with arguments.
978 This macro cannot be evaluated alone without parameters input."
979 ;;TODO: input arguments at run time, use minibuffer to query all arguments
980 (error
981 "Argumented macro cannot be evaluated without passing any parameter"))
982
983 (defun hif-stringify (a)
984 "Stringify a number, string or symbol."
985 (cond
986 ((numberp a)
987 (number-to-string a))
988 ((atom a)
989 (symbol-name a))
990 ((stringp a)
991 (concat "\"" a "\""))
992 (t
993 (error "Invalid token to stringify"))))
994
995 (defun intern-safe (str)
996 (if (stringp str)
997 (intern str)))
998
999 (defun hif-token-concat (a b)
1000 "Concatenate two tokens into a longer token.
1001 Currently support only simple token concatenation. Also support weird (but
1002 valid) token concatenation like `>' ## `>' becomes `>>'. Here we take care only
1003 those that can be evaluated during preprocessing time and ignore all those that
1004 can only be evaluated at C(++) runtime (like `++', `--' and `+='...)."
1005 (if (or (memq a hif-valid-token-list)
1006 (memq b hif-valid-token-list))
1007 (let* ((ra (car (rassq a hif-token-alist)))
1008 (rb (car (rassq b hif-token-alist)))
1009 (result (and ra rb
1010 (cdr (assoc (concat ra rb) hif-token-alist)))))
1011 (or result
1012 ;;(error "Invalid token to concatenate")
1013 (error "Concatenating \"%s\" and \"%s\" does not give a valid \
1014 preprocessing token"
1015 (or ra (symbol-name a))
1016 (or rb (symbol-name b)))))
1017 (intern-safe (concat (hif-stringify a)
1018 (hif-stringify b)))))
1019
1020 (defun hif-mathify (val)
1021 "Treat VAL as a number: if it's t or nil, use 1 or 0."
1022 (cond ((eq val t) 1)
1023 ((null val) 0)
1024 (t val)))
1025
1026 (defun hif-conditional (a b c)
1027 (if (not (zerop (hif-mathify a))) (hif-mathify b) (hif-mathify c)))
1028 (defun hif-and (a b)
1029 (and (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
1030 (defun hif-or (a b)
1031 (or (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
1032 (defun hif-not (a)
1033 (zerop (hif-mathify a)))
1034 (defun hif-lognot (a)
1035 (lognot (hif-mathify a)))
1036
1037 (defmacro hif-mathify-binop (fun)
1038 `(lambda (a b)
1039 ,(format "Like `%s' but treat t and nil as 1 and 0." fun)
1040 (,fun (hif-mathify a) (hif-mathify b))))
1041
1042 (defun hif-shiftleft (a b)
1043 (setq a (hif-mathify a))
1044 (setq b (hif-mathify b))
1045 (if (< a 0)
1046 (ash a b)
1047 (lsh a b)))
1048
1049 (defun hif-shiftright (a b)
1050 (setq a (hif-mathify a))
1051 (setq b (hif-mathify b))
1052 (if (< a 0)
1053 (ash a (- b))
1054 (lsh a (- b))))
1055
1056
1057 (defalias 'hif-multiply (hif-mathify-binop *))
1058 (defalias 'hif-divide (hif-mathify-binop /))
1059 (defalias 'hif-modulo (hif-mathify-binop %))
1060 (defalias 'hif-plus (hif-mathify-binop +))
1061 (defalias 'hif-minus (hif-mathify-binop -))
1062 (defalias 'hif-equal (hif-mathify-binop =))
1063 (defalias 'hif-notequal (hif-mathify-binop /=))
1064 (defalias 'hif-greater (hif-mathify-binop >))
1065 (defalias 'hif-less (hif-mathify-binop <))
1066 (defalias 'hif-greater-equal (hif-mathify-binop >=))
1067 (defalias 'hif-less-equal (hif-mathify-binop <=))
1068 (defalias 'hif-logior (hif-mathify-binop logior))
1069 (defalias 'hif-logxor (hif-mathify-binop logxor))
1070 (defalias 'hif-logand (hif-mathify-binop logand))
1071
1072
1073 (defun hif-comma (&rest expr)
1074 "Evaluate a list of EXPR, return the result of the last item."
1075 (let ((result nil))
1076 (dolist (e expr)
1077 (ignore-errors
1078 (setq result (funcall hide-ifdef-evaluator e))))
1079 result))
1080
1081 (defun hif-token-stringification (l)
1082 "Scan token list for `hif-stringify' ('#') token and stringify the next token."
1083 (let (result)
1084 (while l
1085 (push (if (eq (car l) 'hif-stringify)
1086 (prog1
1087 (if (cadr l)
1088 (hif-stringify (cadr l))
1089 (error "No token to stringify"))
1090 (setq l (cdr l)))
1091 (car l))
1092 result)
1093 (setq l (cdr l)))
1094 (nreverse result)))
1095
1096 (defun hif-token-concatenation (l)
1097 "Scan token list for `hif-token-concat' ('##') token and concatenate two tokens."
1098 (let ((prev nil)
1099 result)
1100 (while l
1101 (while (eq (car l) 'hif-token-concat)
1102 (unless prev
1103 (error "No token before ## to concatenate"))
1104 (unless (cdr l)
1105 (error "No token after ## to concatenate"))
1106 (setq prev (hif-token-concat prev (cadr l)))
1107 (setq l (cddr l)))
1108 (if prev
1109 (setq result (append result (list prev))))
1110 (setq prev (car l)
1111 l (cdr l)))
1112 (if prev
1113 (append result (list prev))
1114 result)))
1115
1116 (defun hif-delimit (lis atom)
1117 (nconc (cl-mapcan (lambda (l) (list l atom))
1118 (butlast lis))
1119 (last lis)))
1120
1121 ;; Perform token replacement:
1122 (defun hif-macro-supply-arguments (macro-name actual-parms)
1123 "Expand a macro call, replace ACTUAL-PARMS in the macro body."
1124 (let* ((SA (assoc macro-name hide-ifdef-env))
1125 (macro (and SA
1126 (cdr SA)
1127 (eq (cadr SA) 'hif-define-macro)
1128 (cddr SA)))
1129 (formal-parms (and macro (car macro)))
1130 (macro-body (and macro (cadr macro)))
1131 actual-count
1132 formal-count
1133 formal
1134 etc)
1135
1136 (when (and actual-parms formal-parms macro-body)
1137 ;; For each actual parameter, evaluate each one and associate it
1138 ;; with an actual parameter, put it into local table and finally
1139 ;; evaluate the macro body.
1140 (if (setq etc (eq (car formal-parms) 'hif-etc))
1141 ;; Take care of `hif-etc' first. Prefix `hif-comma' back if needed.
1142 (setq formal-parms (cdr formal-parms)))
1143 (setq formal-count (length formal-parms)
1144 actual-count (length actual-parms))
1145
1146 (if (> formal-count actual-count)
1147 (error "Too few parameters for macro %S" macro-name)
1148 (if (< formal-count actual-count)
1149 (or etc
1150 (error "Too many parameters for macro %S" macro-name))))
1151
1152 ;; Perform token replacement on the MACRO-BODY with the parameters
1153 (while (setq formal (pop formal-parms))
1154 ;; Prevent repetitive substitution, thus cannot use `subst'
1155 ;; for example:
1156 ;; #define mac(a,b) (a+b)
1157 ;; #define testmac mac(b,y)
1158 ;; testmac should expand to (b+y): replace of argument a and b
1159 ;; occurs simultaneously, not sequentially. If sequentially,
1160 ;; according to the argument order, it will become:
1161 ;; 1. formal parm #1 'a' replaced by actual parm 'b', thus (a+b)
1162 ;; becomes (b+b)
1163 ;; 2. formal parm #2 'b' replaced by actual parm 'y', thus (b+b)
1164 ;; becomes (y+y).
1165 (setq macro-body
1166 ;; Unlike `subst', `substitute' replace only the top level
1167 ;; instead of the whole tree; more importantly, it's not
1168 ;; destructive.
1169 (cl-substitute (if (and etc (null formal-parms))
1170 (hif-delimit actual-parms 'hif-comma)
1171 (car actual-parms))
1172 formal macro-body))
1173 (setq actual-parms (cdr actual-parms)))
1174
1175 ;; Replacement completed, flatten the whole token list
1176 (setq macro-body (hif-flatten macro-body))
1177
1178 ;; Stringification and token concatenation happens here
1179 (hif-token-concatenation (hif-token-stringification macro-body)))))
1180
1181 (defun hif-invoke (macro-name actual-parms)
1182 "Invoke a macro by expanding it, reparse macro-body and finally invoke it."
1183 ;; Reparse the macro body and evaluate it
1184 (funcall hide-ifdef-evaluator
1185 (hif-parse-exp
1186 (hif-macro-supply-arguments macro-name actual-parms)
1187 macro-name)))
1188
1189 ;;;----------- end of parser -----------------------
1190
1191
1192 (defun hif-canonicalize-tokens (regexp) ; For debugging
1193 "Return the expanded result of the scanned tokens."
1194 (save-excursion
1195 (re-search-forward regexp)
1196 (let* ((curr-regexp (match-string 0))
1197 (defined (string-match hif-ifxdef-regexp curr-regexp))
1198 (negate (and defined
1199 (string= (match-string 2 curr-regexp) "n")))
1200 (hif-simple-token-only nil) ;; Dynamic binding var for `hif-tokenize'
1201 (tokens (hif-tokenize (point)
1202 (progn (hif-end-of-line) (point)))))
1203 (if defined
1204 (setq tokens (list 'hif-defined tokens)))
1205 (if negate
1206 (setq tokens (list 'hif-not tokens)))
1207 tokens)))
1208
1209 (defun hif-canonicalize (regexp)
1210 "Return a Lisp expression for its condition by scanning current buffer.
1211 Do this when cursor is at the beginning of `regexp' (i.e. #ifX)."
1212 (let ((case-fold-search nil))
1213 (save-excursion
1214 (re-search-forward regexp)
1215 (let* ((curr-regexp (match-string 0))
1216 (defined (string-match hif-ifxdef-regexp curr-regexp))
1217 (negate (and defined
1218 (string= (match-string 2 curr-regexp) "n")))
1219 (hif-simple-token-only nil) ; Dynamic binding for `hif-tokenize'
1220 (tokens (hif-tokenize (point)
1221 (progn (hif-end-of-line) (point)))))
1222 (if defined
1223 (setq tokens (list 'hif-defined tokens)))
1224 (if negate
1225 (setq tokens (list 'hif-not tokens)))
1226 (hif-parse-exp tokens)))))
1227
1228 (defun hif-find-any-ifX ()
1229 "Move to next #if..., or #ifndef, at point or after."
1230 ;; (message "find ifX at %d" (point))
1231 (prog1
1232 (re-search-forward hif-ifx-regexp (point-max) t)
1233 (beginning-of-line)))
1234
1235
1236 (defun hif-find-next-relevant ()
1237 "Move to next #if..., #elif..., #else, or #endif, after the current line."
1238 ;; (message "hif-find-next-relevant at %d" (point))
1239 (end-of-line)
1240 ;; Avoid infinite recursion by only going to line-beginning if match found
1241 (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
1242 (beginning-of-line)))
1243
1244 (defun hif-find-previous-relevant ()
1245 "Move to previous #if..., #else, or #endif, before the current line."
1246 ;; (message "hif-find-previous-relevant at %d" (point))
1247 (beginning-of-line)
1248 ;; Avoid infinite recursion by only going to line-beginning if match found
1249 (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
1250 (beginning-of-line)))
1251
1252
1253 (defun hif-looking-at-ifX ()
1254 (looking-at hif-ifx-regexp)) ; Should eventually see #if
1255 (defun hif-looking-at-endif ()
1256 (looking-at hif-endif-regexp))
1257 (defun hif-looking-at-else ()
1258 (looking-at hif-else-regexp))
1259
1260 (defun hif-looking-at-elif ()
1261 (looking-at hif-elif-regexp))
1262
1263
1264 (defun hif-ifdef-to-endif ()
1265 "If positioned at #ifX, #elif, or #else form, skip to corresponding #endif."
1266 ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
1267 (hif-find-next-relevant)
1268 (cond ((hif-looking-at-ifX)
1269 (hif-ifdef-to-endif) ; Find endif of nested if
1270 (hif-ifdef-to-endif)) ; Find outer endif or else
1271 ((hif-looking-at-elif)
1272 (hif-ifdef-to-endif))
1273 ((hif-looking-at-else)
1274 (hif-ifdef-to-endif)) ; Find endif following else
1275 ((hif-looking-at-endif)
1276 'done)
1277 (t
1278 (error "Mismatched #ifdef #endif pair"))))
1279
1280
1281 (defun hif-endif-to-ifdef ()
1282 "If positioned at #endif form, skip backward to corresponding #ifX."
1283 ;; (message "hif-endif-to-ifdef at %d" (point))
1284 (let ((start (point)))
1285 (hif-find-previous-relevant)
1286 (if (= start (point))
1287 (error "Mismatched #ifdef #endif pair")))
1288 (cond ((hif-looking-at-endif)
1289 (hif-endif-to-ifdef) ; Find beginning of nested if
1290 (hif-endif-to-ifdef)) ; Find beginning of outer if or else
1291 ((hif-looking-at-elif)
1292 (hif-endif-to-ifdef))
1293 ((hif-looking-at-else)
1294 (hif-endif-to-ifdef))
1295 ((hif-looking-at-ifX)
1296 'done)
1297 (t
1298 (error "Mismatched #endif")))) ; never gets here
1299
1300
1301 (defun forward-ifdef (&optional arg)
1302 "Move point to beginning of line of the next ifdef-endif.
1303 With argument, do this that many times."
1304 (interactive "p")
1305 (or arg (setq arg 1))
1306 (if (< arg 0) (backward-ifdef (- arg))
1307 (while (< 0 arg)
1308 (setq arg (- arg))
1309 (let ((start (point)))
1310 (unless (hif-looking-at-ifX)
1311 (hif-find-next-relevant))
1312 (if (hif-looking-at-ifX)
1313 (hif-ifdef-to-endif)
1314 (goto-char start)
1315 (error "No following #ifdef"))))))
1316
1317
1318 (defun backward-ifdef (&optional arg)
1319 "Move point to beginning of the previous ifdef-endif.
1320 With argument, do this that many times."
1321 (interactive "p")
1322 (or arg (setq arg 1))
1323 (if (< arg 0) (forward-ifdef (- arg))
1324 (while (< 0 arg)
1325 (setq arg (1- arg))
1326 (beginning-of-line)
1327 (let ((start (point)))
1328 (unless (hif-looking-at-endif)
1329 (hif-find-previous-relevant))
1330 (if (hif-looking-at-endif)
1331 (hif-endif-to-ifdef)
1332 (goto-char start)
1333 (error "No previous #ifdef"))))))
1334
1335
1336 (defun down-ifdef ()
1337 "Move point to beginning of nested ifdef or else-part."
1338 (interactive)
1339 (let ((start (point)))
1340 (hif-find-next-relevant)
1341 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
1342 ()
1343 (goto-char start)
1344 (error "No following #ifdef"))))
1345
1346
1347 (defun up-ifdef ()
1348 "Move point to beginning of enclosing ifdef or else-part."
1349 (interactive)
1350 (beginning-of-line)
1351 (let ((start (point)))
1352 (unless (hif-looking-at-endif)
1353 (hif-find-previous-relevant))
1354 (if (hif-looking-at-endif)
1355 (hif-endif-to-ifdef))
1356 (if (= start (point))
1357 (error "No previous #ifdef"))))
1358
1359 (defun next-ifdef (&optional arg)
1360 "Move to the beginning of the next #ifX, #else, or #endif.
1361 With argument, do this that many times."
1362 (interactive "p")
1363 (or arg (setq arg 1))
1364 (if (< arg 0) (previous-ifdef (- arg))
1365 (while (< 0 arg)
1366 (setq arg (1- arg))
1367 (hif-find-next-relevant)
1368 (when (eolp)
1369 (beginning-of-line)
1370 (error "No following #ifdefs, #elses, or #endifs")))))
1371
1372 (defun previous-ifdef (&optional arg)
1373 "Move to the beginning of the previous #ifX, #else, or #endif.
1374 With argument, do this that many times."
1375 (interactive "p")
1376 (or arg (setq arg 1))
1377 (if (< arg 0) (next-ifdef (- arg))
1378 (while (< 0 arg)
1379 (setq arg (1- arg))
1380 (let ((start (point)))
1381 (hif-find-previous-relevant)
1382 (if (= start (point))
1383 (error "No previous #ifdefs, #elses, or #endifs"))))))
1384
1385
1386 ;;===%%SF%% parsing (End) ===
1387
1388
1389 ;;===%%SF%% hide-ifdef-hiding (Start) ===
1390
1391
1392 ;; A range is a structure with four components:
1393 ;; START The start of the range. (beginning of line)
1394 ;; ELSE The else marker (beginning of line)
1395 ;; END The end of the range. (beginning of line)
1396 ;; ELIF A sequence of #elif markers (beginning of line)
1397
1398 (defsubst hif-make-range (start end &optional else elif)
1399 (list start else end elif))
1400
1401 (defsubst hif-range-start (range) (elt range 0))
1402 (defsubst hif-range-else (range) (elt range 1))
1403 (defsubst hif-range-end (range) (elt range 2))
1404 (defsubst hif-range-elif (range) (elt range 3))
1405
1406
1407 ;; Find-Range
1408 ;; The workhorse, it delimits the #if region. Reasonably simple:
1409 ;; Skip until an #else or #endif is found, remembering positions. If
1410 ;; an #else was found, skip some more, looking for the true #endif.
1411
1412 (defun hif-find-range ()
1413 "Return a Range structure describing the current #if region.
1414 Point is left unchanged."
1415 ;; (message "hif-find-range at %d" (point))
1416 (save-excursion
1417 (beginning-of-line)
1418 (let ((start (point))
1419 (elif nil)
1420 (else nil)
1421 (end nil))
1422 ;; Part one. Look for either #elif, #else or #endif.
1423 ;; This loop-and-a-half dedicated to E. Dijkstra.
1424 (while (and (not else) (not end))
1425 (while (progn
1426 (hif-find-next-relevant)
1427 (hif-looking-at-ifX)) ; Skip nested ifdef
1428 (hif-ifdef-to-endif))
1429 ;; Found either a #else, #elif, or an #endif.
1430 (cond ((hif-looking-at-elif)
1431 (setq elif (nconc elif (list (point)))))
1432 ((hif-looking-at-else)
1433 (setq else (point)))
1434 (t
1435 (setq end (point)))))
1436 ;; If found #else, look for #endif.
1437 (when else
1438 (while (progn
1439 (hif-find-next-relevant)
1440 (hif-looking-at-ifX)) ; Skip nested ifdef
1441 (hif-ifdef-to-endif))
1442 (if (hif-looking-at-else)
1443 (error "Found two elses in a row? Broken!"))
1444 (setq end (point))) ; (line-end-position)
1445 (hif-make-range start end else elif))))
1446
1447
1448 ;; A bit slimy.
1449
1450 (defun hif-hide-line (point)
1451 "Hide the line containing point.
1452 Does nothing if `hide-ifdef-lines' is nil."
1453 (when hide-ifdef-lines
1454 (save-excursion
1455 (goto-char point)
1456 (hide-ifdef-region-internal
1457 (line-beginning-position) (progn (hif-end-of-line) (point))))))
1458
1459
1460 ;; Hif-Possibly-Hide
1461 ;; There are four cases. The #ifX expression is "taken" if it
1462 ;; the hide-ifdef-evaluator returns T. Presumably, this means the code
1463 ;; inside the #ifdef would be included when the program was
1464 ;; compiled.
1465 ;;
1466 ;; Case 1: #ifX taken, and there's an #else.
1467 ;; The #else part must be hidden. The #if (then) part must be
1468 ;; processed for nested #ifX's.
1469 ;; Case 2: #ifX taken, and there's no #else.
1470 ;; The #if part must be processed for nested #ifX's.
1471 ;; Case 3: #ifX not taken, and there's an #elif
1472 ;; The #if part must be hidden, and then evaluate
1473 ;; the #elif condition like a new #ifX.
1474 ;; Case 4: #ifX not taken, and there's just an #else.
1475 ;; The #if part must be hidden. The #else part must be processed
1476 ;; for nested #ifs.
1477 ;; Case 5: #ifX not taken, and there's no #else.
1478 ;; The #ifX part must be hidden.
1479 ;;
1480 ;; Further processing is done by narrowing to the relevant region
1481 ;; and just recursively calling hide-ifdef-guts.
1482 ;;
1483 ;; When hif-possibly-hide returns, point is at the end of the
1484 ;; possibly-hidden range.
1485
1486 (defvar hif-recurse-level 0)
1487
1488 (defun hif-recurse-on (start end &optional dont-go-eol)
1489 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
1490 (save-excursion
1491 (save-restriction
1492 (goto-char start)
1493 (unless dont-go-eol
1494 (end-of-line))
1495 (narrow-to-region (point) end)
1496 (let ((hif-recurse-level (1+ hif-recurse-level)))
1497 (hide-ifdef-guts)))))
1498
1499 (defun hif-possibly-hide (expand-reinclusion)
1500 "Called at #ifX expression, this hides those parts that should be hidden.
1501 It uses the judgment of `hide-ifdef-evaluator'. EXPAND-REINCLUSION is a flag
1502 indicating that we should expand the #ifdef even if it should be hidden.
1503 Refer to `hide-ifdef-expand-reinclusion-protection' for more details."
1504 ;; (message "hif-possibly-hide") (sit-for 1)
1505 (let* ((case-fold-search nil)
1506 (test (hif-canonicalize hif-ifx-regexp))
1507 (range (hif-find-range))
1508 (elifs (hif-range-elif range))
1509 (if-part t) ; Everytime we start from if-part
1510 (complete nil))
1511 ;; (message "test = %s" test) (sit-for 1)
1512
1513 (hif-hide-line (hif-range-end range))
1514 (while (not complete)
1515 (if (and (not (and expand-reinclusion if-part))
1516 (hif-not (funcall hide-ifdef-evaluator test)))
1517 ;; ifX/elif is FALSE
1518 (if elifs
1519 ;; Case 3 - Hide the #ifX and eval #elif
1520 (let ((newstart (car elifs)))
1521 (hif-hide-line (hif-range-start range))
1522 (hide-ifdef-region (hif-range-start range)
1523 (1- newstart))
1524 (setcar range newstart)
1525 (goto-char newstart)
1526 (setq elifs (cdr elifs))
1527 (setq test (hif-canonicalize hif-elif-regexp)))
1528
1529 ;; Check for #else
1530 (cond ((hif-range-else range)
1531 ;; Case 4 - #else block visible
1532 (hif-hide-line (hif-range-else range))
1533 (hide-ifdef-region (hif-range-start range)
1534 (1- (hif-range-else range)))
1535 (hif-recurse-on (hif-range-else range)
1536 (hif-range-end range)))
1537 (t
1538 ;; Case 5 - No #else block, hide #ifX
1539 (hide-ifdef-region (point)
1540 (1- (hif-range-end range)))))
1541 (setq complete t))
1542
1543 ;; ifX/elif is TRUE
1544 (cond (elifs
1545 ;; Luke fix: distinguish from #elif..#elif to #elif..#else
1546 (let ((elif (car elifs)))
1547 ;; hide all elifs
1548 (hif-hide-line elif)
1549 (hide-ifdef-region elif (1- (hif-range-end range)))
1550 (hif-recurse-on (hif-range-start range)
1551 elif)))
1552 ((hif-range-else range)
1553 ;; Case 1 - Hide #elif and #else blocks, recurse #ifX
1554 (hif-hide-line (hif-range-else range))
1555 (hide-ifdef-region (hif-range-else range)
1556 (1- (hif-range-end range)))
1557 (hif-recurse-on (hif-range-start range)
1558 (hif-range-else range)))
1559 (t
1560 ;; Case 2 - No #else, just recurse #ifX
1561 (hif-recurse-on (hif-range-start range)
1562 (hif-range-end range))))
1563 (setq complete t))
1564 (setq if-part nil))
1565
1566 ;; complete = t
1567 (hif-hide-line (hif-range-start range)) ; Always hide start.
1568 (goto-char (hif-range-end range))
1569 (end-of-line)))
1570
1571 (defun hif-evaluate-region (start end)
1572 (let* ((tokens (ignore-errors ; Prevent C statement things like
1573 ; 'do { ... } while (0)'
1574 (hif-tokenize start end)))
1575 (expr (and tokens
1576 (condition-case nil
1577 (hif-parse-exp tokens)
1578 (error
1579 tokens))))
1580 (result (funcall hide-ifdef-evaluator expr)))
1581 result))
1582
1583 (defun hif-evaluate-macro (rstart rend)
1584 "Evaluate the macro expansion result for a region.
1585 If no region active, find the current #ifdefs and evaluate the result.
1586 Currently it supports only math calculations, strings or argumented macros can
1587 not be expanded."
1588 (interactive "r")
1589 (let ((case-fold-search nil))
1590 (save-excursion
1591 (unless mark-active
1592 (setq rstart nil rend nil)
1593 (beginning-of-line)
1594 (when (and (re-search-forward hif-macro-expr-prefix-regexp nil t)
1595 (string= "define" (match-string 2)))
1596 (re-search-forward hif-macroref-regexp nil t)))
1597 (let* ((start (or rstart (point)))
1598 (end (or rend (progn (hif-end-of-line) (point))))
1599 (defined nil)
1600 (simple 't)
1601 (tokens (ignore-errors ; Prevent C statement things like
1602 ; 'do { ... } while (0)'
1603 (hif-tokenize start end)))
1604 (expr (or (and (<= (length tokens) 1) ; Simple token
1605 (setq defined (assoc (car tokens) hide-ifdef-env))
1606 (setq simple (atom (hif-lookup (car tokens))))
1607 (hif-lookup (car tokens)))
1608 (and tokens
1609 (condition-case nil
1610 (hif-parse-exp tokens)
1611 (error
1612 nil)))))
1613 (result (funcall hide-ifdef-evaluator expr))
1614 (exprstring (replace-regexp-in-string
1615 ;; Trim off leading/trailing whites
1616 "^[ \t]*\\([^ \t]+\\)[ \t]*" "\\1"
1617 (replace-regexp-in-string
1618 "\\(//.*\\)" "" ; Trim off end-of-line comments
1619 (buffer-substring-no-properties start end)))))
1620 (cond
1621 ((and (<= (length tokens) 1) simple) ; Simple token
1622 (if defined
1623 (message "%S <= `%s'" result exprstring)
1624 (message "`%s' is not defined" exprstring)))
1625 ((integerp result)
1626 (if (or (= 0 result) (= 1 result))
1627 (message "%S <= `%s'" result exprstring)
1628 (message "%S (0x%x) <= `%s'" result result exprstring)))
1629 ((null result) (message "%S <= `%s'" 'false exprstring))
1630 ((eq t result) (message "%S <= `%s'" 'true exprstring))
1631 (t (message "%S <= `%s'" result exprstring)))
1632 result))))
1633
1634 (defun hif-parse-macro-arglist (str)
1635 "Parse argument list formatted as `( arg1 [ , argn] [...] )'.
1636 The `...' is also included. Return a list of the arguments, if `...' exists the
1637 first arg will be `hif-etc'."
1638 (let* ((hif-simple-token-only nil) ; Dynamic binding var for `hif-tokenize'
1639 (tokenlist
1640 (cdr (hif-tokenize
1641 (- (point) (length str)) (point)))) ; Remove `hif-lparen'
1642 etc result token)
1643 (while (not (eq (setq token (pop tokenlist)) 'hif-rparen))
1644 (cond
1645 ((eq token 'hif-etc)
1646 (setq etc t))
1647 ((eq token 'hif-comma)
1648 t)
1649 (t
1650 (push token result))))
1651 (if etc
1652 (cons 'hif-etc (nreverse result))
1653 (nreverse result))))
1654
1655 ;; The original version of hideif evaluates the macro early and store the
1656 ;; final values for the defined macro into the symbol database (aka
1657 ;; `hide-ifdef-env'). The evaluation process is "strings -> tokens -> parsed
1658 ;; tree -> [value]". (The square bracket refers to what's stored in in our
1659 ;; `hide-ifdef-env'.)
1660 ;;
1661 ;; This forbids the evaluation of an argumented macro since the parameters
1662 ;; are applied at run time. In order to support argumented macro I then
1663 ;; postponed the evaluation process one stage and store the "parsed tree"
1664 ;; into symbol database. The evaluation process was then "strings -> tokens
1665 ;; -> [parsed tree] -> value". Hideif therefore run slower since it need to
1666 ;; evaluate the parsed tree everytime when trying to expand the symbol. These
1667 ;; temporarily code changes are obsolete and not in Emacs source repository.
1668 ;;
1669 ;; Furthermore, CPP did allow partial expression to be defined in several
1670 ;; macros and later got concatenated into a complete expression and then
1671 ;; evaluate it. In order to match this behavior I had to postpone one stage
1672 ;; further, otherwise those partial expression will be fail on parsing and
1673 ;; we'll miss all macros that reference it. The evaluation process thus
1674 ;; became "strings -> [tokens] -> parsed tree -> value." This degraded the
1675 ;; performance since we need to parse tokens and evaluate them everytime
1676 ;; when that symbol is referenced.
1677 ;;
1678 ;; In real cases I found a lot portion of macros are "simple macros" that
1679 ;; expand to literals like integers or other symbols. In order to enhance
1680 ;; the performance I use this `hif-simple-token-only' to notify my code and
1681 ;; save the final [value] into symbol database. [lukelee]
1682
1683 (defun hif-find-define (&optional min max)
1684 "Parse texts and retrieve all defines within the region MIN and MAX."
1685 (interactive)
1686 (and min (goto-char min))
1687 (and (re-search-forward hif-define-regexp max t)
1688 (or
1689 (let* ((defining (string= "define" (match-string 2)))
1690 (name (and (re-search-forward hif-macroref-regexp max t)
1691 (match-string 1)))
1692 (parmlist (and (match-string 3) ; First arg id found
1693 (hif-parse-macro-arglist (match-string 2)))))
1694 (if defining
1695 ;; Ignore name (still need to return 't), or define the name
1696 (or (and hide-ifdef-exclude-define-regexp
1697 (string-match hide-ifdef-exclude-define-regexp
1698 name))
1699
1700 (let* ((start (point))
1701 (end (progn (hif-end-of-line) (point)))
1702 (hif-simple-token-only nil) ; Dynamic binding
1703 (tokens
1704 (and name
1705 ;; `hif-simple-token-only' is set/clear
1706 ;; only in this block
1707 (condition-case nil
1708 ;; Prevent C statements like
1709 ;; 'do { ... } while (0)'
1710 (hif-tokenize start end)
1711 (error
1712 ;; We can't just return nil here since
1713 ;; this will stop hideif from searching
1714 ;; for more #defines.
1715 (setq hif-simple-token-only t)
1716 (buffer-substring-no-properties
1717 start end)))))
1718 ;; For simple tokens we save only the parsed result;
1719 ;; otherwise we save the tokens and parse it after
1720 ;; parameter replacement
1721 (expr (and tokens
1722 ;; `hif-simple-token-only' is checked only
1723 ;; here.
1724 (or (and hif-simple-token-only
1725 (listp tokens)
1726 (= (length tokens) 1)
1727 (hif-parse-exp tokens))
1728 `(hif-define-macro ,parmlist
1729 ,tokens))))
1730 (SA (and name
1731 (assoc (intern name) hide-ifdef-env))))
1732 (and name
1733 (if SA
1734 (or (setcdr SA expr) t)
1735 ;; Lazy evaluation, eval only if hif-lookup find it.
1736 ;; Define it anyway, even if nil it's still in list
1737 ;; and therefore considered defined.
1738 (push (cons (intern name) expr) hide-ifdef-env)))))
1739 ;; #undef
1740 (and name
1741 (hif-undefine-symbol (intern name))))))
1742 t))
1743
1744
1745 (defun hif-add-new-defines (&optional min max)
1746 "Scan and add all #define macros between MIN and MAX."
1747 (interactive)
1748 (save-excursion
1749 (save-restriction
1750 ;; (mark-region min max) ;; for debugging
1751 (while (hif-find-define min max)
1752 (setf min (point)))
1753 (if max (goto-char max)
1754 (goto-char (point-max))))))
1755
1756 (defun hide-ifdef-guts ()
1757 "Does most of the work of `hide-ifdefs'.
1758 It does not do the work that's pointless to redo on a recursive entry."
1759 ;; (message "hide-ifdef-guts")
1760 (save-excursion
1761 (let* ((case-fold-search t) ; Ignore case for `hide-ifdef-header-regexp'
1762 (expand-header (and hide-ifdef-expand-reinclusion-protection
1763 (string-match hide-ifdef-header-regexp
1764 (buffer-file-name))
1765 (zerop hif-recurse-level)))
1766 (case-fold-search nil)
1767 min max)
1768 (goto-char (point-min))
1769 (setf min (point))
1770 (cl-loop do
1771 (setf max (hif-find-any-ifX))
1772 (hif-add-new-defines min max)
1773 (if max
1774 (hif-possibly-hide expand-header))
1775 (setf min (point))
1776 while max))))
1777
1778 ;;===%%SF%% hide-ifdef-hiding (End) ===
1779
1780
1781 ;;===%%SF%% exports (Start) ===
1782
1783 (defun hide-ifdef-toggle-read-only ()
1784 "Toggle `hide-ifdef-read-only'."
1785 (interactive)
1786 (setq hide-ifdef-read-only (not hide-ifdef-read-only))
1787 (message "Hide-Read-Only %s"
1788 (if hide-ifdef-read-only "ON" "OFF"))
1789 (if hide-ifdef-hiding
1790 (setq buffer-read-only (or hide-ifdef-read-only
1791 hif-outside-read-only)))
1792 (force-mode-line-update))
1793
1794 (defun hide-ifdef-toggle-outside-read-only ()
1795 "Replacement for `read-only-mode' within Hide-Ifdef mode."
1796 (interactive)
1797 (setq hif-outside-read-only (not hif-outside-read-only))
1798 (message "Read only %s"
1799 (if hif-outside-read-only "ON" "OFF"))
1800 (setq buffer-read-only
1801 (or (and hide-ifdef-hiding hide-ifdef-read-only)
1802 hif-outside-read-only))
1803 (force-mode-line-update))
1804
1805 (defun hide-ifdef-toggle-shadowing ()
1806 "Toggle shadowing."
1807 (interactive)
1808 (set (make-local-variable 'hide-ifdef-shadow) (not hide-ifdef-shadow))
1809 (message "Shadowing %s" (if hide-ifdef-shadow "ON" "OFF"))
1810 (save-restriction
1811 (widen)
1812 (dolist (overlay (overlays-in (point-min) (point-max)))
1813 (when (overlay-get overlay 'hide-ifdef)
1814 (if hide-ifdef-shadow
1815 (progn
1816 (overlay-put overlay 'invisible nil)
1817 (overlay-put overlay 'face 'hide-ifdef-shadow))
1818 (overlay-put overlay 'face nil)
1819 (overlay-put overlay 'invisible 'hide-ifdef))))))
1820
1821 (defun hide-ifdef-define (var &optional val)
1822 "Define a VAR to VAL (default 1) in `hide-ifdef-env'.
1823 This allows #ifdef VAR to be hidden."
1824 (interactive
1825 (let* ((default (save-excursion
1826 (beginning-of-line)
1827 (cond ((looking-at hif-ifx-else-endif-regexp)
1828 (forward-word 2)
1829 (current-word 'strict))
1830 (t
1831 nil))))
1832 (var (read-minibuffer "Define what? " default))
1833 (val (read-from-minibuffer (format "Set %s to? (default 1): " var)
1834 nil nil t nil "1")))
1835 (list var val)))
1836 (hif-set-var var (or val 1))
1837 (message "%s set to %s" var (or val 1))
1838 (sleep-for 1)
1839 (if hide-ifdef-hiding (hide-ifdefs)))
1840
1841 (defun hif-undefine-symbol (var)
1842 (setq hide-ifdef-env
1843 (delete (assoc var hide-ifdef-env) hide-ifdef-env)))
1844
1845 (defun hide-ifdef-undef (start end)
1846 "Undefine a VAR so that #ifdef VAR would not be included."
1847 (interactive "r")
1848 (let* ((symstr
1849 (or (and mark-active
1850 (buffer-substring-no-properties start end))
1851 (read-string "Undefine what? " (current-word))))
1852 (sym (and symstr
1853 (intern symstr))))
1854 (if (zerop (hif-defined sym))
1855 (message "`%s' not defined, no need to undefine it" symstr)
1856 (hif-undefine-symbol sym)
1857 (if hide-ifdef-hiding (hide-ifdefs))
1858 (message "`%S' undefined" sym))))
1859
1860 (defun hide-ifdefs (&optional nomsg)
1861 "Hide the contents of some #ifdefs.
1862 Assume that defined symbols have been added to `hide-ifdef-env'.
1863 The text hidden is the text that would not be included by the C
1864 preprocessor if it were given the file with those symbols defined.
1865 With prefix command presents it will also hide the #ifdefs themselves.
1866
1867 Turn off hiding by calling `show-ifdefs'."
1868
1869 (interactive)
1870 (let ((hide-ifdef-lines current-prefix-arg))
1871 (or nomsg
1872 (message "Hiding..."))
1873 (setq hif-outside-read-only buffer-read-only)
1874 (unless hide-ifdef-mode (hide-ifdef-mode 1)) ; Turn on hide-ifdef-mode
1875 (if hide-ifdef-hiding
1876 (show-ifdefs)) ; Otherwise, deep confusion.
1877 (setq hide-ifdef-hiding t)
1878 (hide-ifdef-guts)
1879 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only))
1880 (or nomsg
1881 (message "Hiding done"))))
1882
1883
1884 (defun show-ifdefs ()
1885 "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
1886 (interactive)
1887 (setq buffer-read-only hif-outside-read-only)
1888 (hif-show-all)
1889 (setq hide-ifdef-hiding nil))
1890
1891
1892 (defun hif-find-ifdef-block ()
1893 "Utility to hide and show ifdef block.
1894 Return as (TOP . BOTTOM) the extent of ifdef block."
1895 (let (max-bottom)
1896 (cons (save-excursion
1897 (beginning-of-line)
1898 (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
1899 (up-ifdef))
1900 (prog1 (point)
1901 (hif-ifdef-to-endif)
1902 (setq max-bottom (1- (point)))))
1903 (save-excursion
1904 (beginning-of-line)
1905 (unless (hif-looking-at-endif)
1906 (hif-find-next-relevant))
1907 (while (hif-looking-at-ifX)
1908 (hif-ifdef-to-endif)
1909 (hif-find-next-relevant))
1910 (min max-bottom (1- (point)))))))
1911
1912
1913 (defun hide-ifdef-block (&optional arg start end)
1914 "Hide the ifdef block (true or false part) enclosing or before the cursor.
1915 With optional prefix argument ARG, also hide the #ifdefs themselves."
1916 (interactive "P\nr")
1917 (let ((hide-ifdef-lines arg))
1918 (if mark-active
1919 (let ((hif-recurse-level (1+ hif-recurse-level)))
1920 (hif-recurse-on start end t)
1921 (setq mark-active nil))
1922 (unless hide-ifdef-mode (hide-ifdef-mode 1))
1923 (let ((top-bottom (hif-find-ifdef-block)))
1924 (hide-ifdef-region (car top-bottom) (cdr top-bottom))
1925 (when hide-ifdef-lines
1926 (hif-hide-line (car top-bottom))
1927 (hif-hide-line (1+ (cdr top-bottom))))
1928 (setq hide-ifdef-hiding t))
1929 (setq buffer-read-only
1930 (or hide-ifdef-read-only hif-outside-read-only)))))
1931
1932 (defun show-ifdef-block (&optional start end)
1933 "Show the ifdef block (true or false part) enclosing or before the cursor."
1934 (interactive "r")
1935 (if mark-active
1936 (progn
1937 (dolist (o (overlays-in start end))
1938 (if (overlay-get o 'hide-ifdef)
1939 (delete-overlay o)))
1940 (setq mark-active nil))
1941 (let ((top-bottom (condition-case nil
1942 (hif-find-ifdef-block)
1943 (error
1944 nil)))
1945 (ovrs (overlays-in (max (point-min) (1- (point)))
1946 (min (point-max) (1+ (point)))))
1947 (del nil))
1948 (if top-bottom
1949 (if hide-ifdef-lines
1950 (hif-show-ifdef-region
1951 (save-excursion
1952 (goto-char (car top-bottom)) (line-beginning-position))
1953 (save-excursion
1954 (goto-char (1+ (cdr top-bottom)))
1955 (hif-end-of-line) (point)))
1956 (setf del (hif-show-ifdef-region
1957 (1- (car top-bottom)) (cdr top-bottom)))))
1958 (if (not (and top-bottom
1959 del))
1960 (dolist (o ovrs)
1961 ;;(dolist (o (overlays-in (1- (point)) (1+ (point))))
1962 ;; (if (overlay-get o 'hide-ifdef) (message "%S" o)))
1963 (if (overlay-get o 'hide-ifdef)
1964 (delete-overlay o)))))))
1965
1966
1967 ;;; definition alist support
1968
1969 (defvar hide-ifdef-define-alist nil
1970 "A global assoc list of pre-defined symbol lists.")
1971
1972 (defun hif-compress-define-list (env)
1973 "Compress the define list ENV into a list of defined symbols only."
1974 (let ((new-defs nil))
1975 (dolist (def env new-defs)
1976 (if (hif-lookup (car def)) (push (car def) new-defs)))))
1977
1978 (defun hide-ifdef-set-define-alist (name)
1979 "Set the association for NAME to `hide-ifdef-env'."
1980 (interactive "SSet define list: ")
1981 (push (cons name (hif-compress-define-list hide-ifdef-env))
1982 hide-ifdef-define-alist))
1983
1984 (defun hide-ifdef-use-define-alist (name)
1985 "Set `hide-ifdef-env' to the define list specified by NAME."
1986 (interactive
1987 (list (completing-read "Use define list: "
1988 (mapcar (lambda (x) (symbol-name (car x)))
1989 hide-ifdef-define-alist)
1990 nil t)))
1991 (if (stringp name) (setq name (intern name)))
1992 (let ((define-list (assoc name hide-ifdef-define-alist)))
1993 (if define-list
1994 (setq hide-ifdef-env
1995 (mapcar (lambda (arg) (cons arg t))
1996 (cdr define-list)))
1997 (error "No define list for %s" name))
1998 (if hide-ifdef-hiding (hide-ifdefs))))
1999
2000 (provide 'hideif)
2001
2002 ;;; hideif.el ends here