* lisp/progmodes/hideif.el (hif-string-to-number): Don't return float for
[bpt/emacs.git] / lisp / progmodes / hideif.el
CommitLineData
55535639 1;;; hideif.el --- hides selected code within ifdef
fc68affa 2
ba318903 3;; Copyright (C) 1988, 1994, 2001-2014 Free Software Foundation, Inc.
3a801d0c 4
6b13eef0
GM
5;; Author: Brian Marick
6;; Daniel LaLiberte <liberte@holonexus.org>
34dc21db 7;; Maintainer: emacs-devel@gnu.org
612abcae 8;; Keywords: c, outlines
fc68affa 9
c9ed5a47
RS
10;; This file is part of GNU Emacs.
11
b1fc2b50 12;; GNU Emacs is free software: you can redistribute it and/or modify
c9ed5a47 13;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
c9ed5a47
RS
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
b1fc2b50 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
c9ed5a47 24
fc68affa
ER
25;;; Commentary:
26
b578f267
EN
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
a9128931 38;; pass through. Support complete C/C++ expression and precedence.
b578f267
EN
39;;
40;; The hidden code is marked by ellipses (...). Be
41;; cautious when editing near ellipses, since the hidden text is
42;; still in the buffer, and you can move the point into it and modify
067d92a1 43;; text unawares.
b578f267 44;; You can make your buffer read-only while hide-ifdef-hiding by setting
a1506d29 45;; hide-ifdef-read-only to a non-nil value. You can toggle this
b578f267
EN
46;; variable with hide-ifdef-toggle-read-only (C-c @ C-q).
47;;
48;; You can undo the effect of hide-ifdefs by typing
49;;
50;; M-x show-ifdefs or C-c @ s
51;;
52;; Use M-x hide-ifdef-define (C-c @ d) to define a symbol.
53;; Use M-x hide-ifdef-undef (C-c @ u) to undefine a symbol.
54;;
55;; If you define or undefine a symbol while hide-ifdef-mode is in effect,
56;; the display will be updated. Only the define list for the current
57;; buffer will be affected. You can save changes to the local define
a1506d29 58;; list with hide-ifdef-set-define-alist. This adds entries
b578f267
EN
59;; to hide-ifdef-define-alist.
60;;
61;; If you have defined a hide-ifdef-mode-hook, you can set
62;; up a list of symbols that may be used by hide-ifdefs as in the
63;; following example:
64;;
067d92a1 65;; (add-hook 'hide-ifdef-mode-hook
16fdbdce 66;; (lambda ()
067d92a1
SM
67;; (unless hide-ifdef-define-alist
68;; (setq hide-ifdef-define-alist
69;; '((list1 ONE TWO)
70;; (list2 TWO THREE))))
71;; (hide-ifdef-use-define-alist 'list2))) ; use list2 by default
b578f267 72;;
482bb01b 73;; You can call hide-ifdef-use-define-alist (C-c @ U) at any time to specify
b578f267
EN
74;; another list to use.
75;;
76;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
77;; set hide-ifdef-initially to non-nil.
78;;
79;; If you set hide-ifdef-lines to t, hide-ifdefs hides all the #ifdef lines.
80;; In the absence of highlighting, that might be a bad idea. If you set
81;; hide-ifdef-lines to nil (the default), the surrounding preprocessor
82;; lines will be displayed. That can be confusing in its own
83;; right. Other variations on display are possible, but not much
84;; better.
85;;
86;; You can explicitly hide or show individual ifdef blocks irrespective
87;; of the define list by using hide-ifdef-block and show-ifdef-block.
88;;
89;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
90;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
91;;
92;; If you have minor-mode-alist in your mode line (the default) two labels
93;; may appear. "Ifdef" will appear when hide-ifdef-mode is active. "Hiding"
94;; will appear when text may be hidden ("hide-ifdef-hiding" is non-nil).
95;;
96;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
97;; Extensively modified by Daniel LaLiberte (while at Gould).
a9128931
LL
98;;
99;; Extensively modified by Luke Lee in 2013 to support complete C expression
100;; evaluation.
175ce218 101
fc68affa 102;;; Code:
175ce218 103
048fb1b7
RS
104(require 'cc-mode)
105
28d16ed3
AS
106(defgroup hide-ifdef nil
107 "Hide selected code within `ifdef'."
108 :group 'c)
109
adcc05dc
GM
110(defcustom hide-ifdef-initially nil
111 "Non-nil means call `hide-ifdefs' when Hide-Ifdef mode is first activated."
112 :type 'boolean
113 :group 'hide-ifdef)
114
adcc05dc
GM
115(defcustom hide-ifdef-read-only nil
116 "Set to non-nil if you want buffer to be read-only while hiding text."
117 :type 'boolean
118 :group 'hide-ifdef)
119
adcc05dc
GM
120(defcustom hide-ifdef-lines nil
121 "Non-nil means hide the #ifX, #else, and #endif lines."
122 :type 'boolean
123 :group 'hide-ifdef)
124
adcc05dc
GM
125(defcustom hide-ifdef-shadow nil
126 "Non-nil means shadow text instead of hiding it."
127 :type 'boolean
c2334613
MR
128 :group 'hide-ifdef
129 :version "23.1")
adcc05dc 130
adcc05dc
GM
131(defface hide-ifdef-shadow '((t (:inherit shadow)))
132 "Face for shadowing ifdef blocks."
c2334613
MR
133 :group 'hide-ifdef
134 :version "23.1")
adcc05dc
GM
135
136
067d92a1
SM
137(defvar hide-ifdef-mode-submap
138 ;; Set up the submap that goes after the prefix key.
139 (let ((map (make-sparse-keymap)))
140 (define-key map "d" 'hide-ifdef-define)
141 (define-key map "u" 'hide-ifdef-undef)
142 (define-key map "D" 'hide-ifdef-set-define-alist)
143 (define-key map "U" 'hide-ifdef-use-define-alist)
144
145 (define-key map "h" 'hide-ifdefs)
146 (define-key map "s" 'show-ifdefs)
147 (define-key map "\C-d" 'hide-ifdef-block)
148 (define-key map "\C-s" 'show-ifdef-block)
149
150 (define-key map "\C-q" 'hide-ifdef-toggle-read-only)
f3a221cf 151 (define-key map "\C-w" 'hide-ifdef-toggle-shadowing)
067d92a1
SM
152 (substitute-key-definition
153 'toggle-read-only 'hide-ifdef-toggle-outside-read-only map)
154 map)
155 "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'.")
175ce218 156
0f21f770 157(defconst hide-ifdef-mode-prefix-key "\C-c@"
073c9531 158 "Prefix key for all Hide-Ifdef mode commands.")
175ce218 159
067d92a1
SM
160(defvar hide-ifdef-mode-map
161 ;; Set up the mode's main map, which leads via the prefix key to the submap.
162 (let ((map (make-sparse-keymap)))
163 (define-key map hide-ifdef-mode-prefix-key hide-ifdef-mode-submap)
164 map)
165 "Keymap used with `hide-ifdef-mode'.")
175ce218 166
47aef245
NR
167(easy-menu-define hide-ifdef-mode-menu hide-ifdef-mode-map
168 "Menu for `hide-ifdef-mode'."
169 '("Hide-Ifdef"
61acee99
DN
170 ["Hide some ifdefs" hide-ifdefs
171 :help "Hide the contents of some #ifdefs"]
172 ["Show all ifdefs" show-ifdefs
173 :help "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs"]
174 ["Hide ifdef block" hide-ifdef-block
175 :help "Hide the ifdef block (true or false part) enclosing or before the cursor"]
176 ["Show ifdef block" show-ifdef-block
177 :help "Show the ifdef block (true or false part) enclosing or before the cursor"]
178 ["Define a variable..." hide-ifdef-define
179 :help "Define a VAR so that #ifdef VAR would be included"]
180 ["Undefine a variable..." hide-ifdef-undef
181 :help "Undefine a VAR so that #ifdef VAR would not be included"]
182 ["Define an alist..." hide-ifdef-set-define-alist
183 :help "Set the association for NAME to `hide-ifdef-env'"]
184 ["Use an alist..." hide-ifdef-use-define-alist
185 :help "Set `hide-ifdef-env' to the define list specified by NAME"]
47aef245 186 ["Toggle read only" hide-ifdef-toggle-read-only
61acee99
DN
187 :style toggle :selected hide-ifdef-read-only
188 :help "Buffer should be read-only while hiding text"]
f3a221cf 189 ["Toggle shadowing" hide-ifdef-toggle-shadowing
61acee99
DN
190 :style toggle :selected hide-ifdef-shadow
191 :help "Text should be shadowed instead of hidden"]))
47aef245 192
175ce218 193(defvar hide-ifdef-hiding nil
36f063e8
RS
194 "Non-nil when text may be hidden.")
195
175ce218
RS
196(or (assq 'hide-ifdef-hiding minor-mode-alist)
197 (setq minor-mode-alist
198 (cons '(hide-ifdef-hiding " Hiding")
199 minor-mode-alist)))
200
e945e87d
KH
201;; fix c-mode syntax table so we can recognize whole symbols.
202(defvar hide-ifdef-syntax-table
067d92a1
SM
203 (let ((st (copy-syntax-table c-mode-syntax-table)))
204 (modify-syntax-entry ?_ "w" st)
205 (modify-syntax-entry ?& "." st)
206 (modify-syntax-entry ?\| "." st)
207 st)
e945e87d
KH
208 "Syntax table used for tokenizing #if expressions.")
209
4e391a67
AS
210(defvar hide-ifdef-env nil
211 "An alist of defined symbols and their values.")
212
213(defvar hif-outside-read-only nil
214 "Internal variable. Saves the value of `buffer-read-only' while hiding.")
215
fbfed6f0 216;;;###autoload
067d92a1 217(define-minor-mode hide-ifdef-mode
ac6c8639
CY
218 "Toggle features to hide/show #ifdef blocks (Hide-Ifdef mode).
219With a prefix argument ARG, enable Hide-Ifdef mode if ARG is
220positive, and disable it otherwise. If called from Lisp, enable
221the mode if ARG is omitted or nil.
222
223Hide-Ifdef mode is a buffer-local minor mode for use with C and
224C-like major modes. When enabled, code within #ifdef constructs
225that the C preprocessor would eliminate may be hidden from view.
226Several variables affect how the hiding is done:
175ce218 227
067d92a1 228`hide-ifdef-env'
175ce218 229 An association list of defined and undefined symbols for the
073c9531
JB
230 current buffer. Initially, the global value of `hide-ifdef-env'
231 is used.
175ce218 232
067d92a1 233`hide-ifdef-define-alist'
a1506d29 234 An association list of defined symbol lists.
073c9531
JB
235 Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
236 and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
237 from one of the lists in `hide-ifdef-define-alist'.
175ce218 238
067d92a1 239`hide-ifdef-lines'
175ce218
RS
240 Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
241 #endif lines when hiding.
242
067d92a1 243`hide-ifdef-initially'
073c9531 244 Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
175ce218
RS
245 is activated.
246
067d92a1 247`hide-ifdef-read-only'
175ce218 248 Set to non-nil if you want to make buffers read only while hiding.
073c9531 249 After `show-ifdefs', read-only status is restored to previous value.
175ce218
RS
250
251\\{hide-ifdef-mode-map}"
0f06a4df 252 :group 'hide-ifdef :lighter " Ifdef"
175ce218
RS
253 (if hide-ifdef-mode
254 (progn
067d92a1
SM
255 ;; inherit global values
256 (set (make-local-variable 'hide-ifdef-env)
257 (default-value 'hide-ifdef-env))
258 (set (make-local-variable 'hide-ifdef-hiding)
259 (default-value 'hide-ifdef-hiding))
260 (set (make-local-variable 'hif-outside-read-only) buffer-read-only)
722fa77f 261 (set (make-local-variable 'line-move-ignore-invisible) t)
f71d927d
SM
262 (add-hook 'change-major-mode-hook
263 (lambda () (hide-ifdef-mode -1)) nil t)
175ce218 264
067d92a1 265 (add-to-invisibility-spec '(hide-ifdef . t))
175ce218
RS
266
267 (if hide-ifdef-initially
268 (hide-ifdefs)
067d92a1
SM
269 (show-ifdefs)))
270 ;; else end hide-ifdef-mode
722fa77f 271 (kill-local-variable 'line-move-ignore-invisible)
f71d927d 272 (remove-from-invisibility-spec '(hide-ifdef . t))
61acee99
DN
273 (when hide-ifdef-hiding
274 (show-ifdefs))))
a1506d29 275
175ce218 276
175ce218
RS
277(defun hif-show-all ()
278 "Show all of the text in the current buffer."
279 (interactive)
067d92a1 280 (hif-show-ifdef-region (point-min) (point-max)))
175ce218 281
f5356416
RS
282;; By putting this on after-revert-hook, we arrange that it only
283;; does anything when revert-buffer avoids turning off the mode.
284;; (That can happen in VC.)
067d92a1 285(defun hif-after-revert-function ()
f5356416
RS
286 (and hide-ifdef-mode hide-ifdef-hiding
287 (hide-ifdefs t)))
067d92a1 288(add-hook 'after-revert-hook 'hif-after-revert-function)
f5356416 289
722fa77f
SM
290(defun hif-end-of-line ()
291 (end-of-line)
292 (while (= (logand 1 (skip-chars-backward "\\\\")) 1)
293 (end-of-line 2)))
294
295(defun hide-ifdef-region-internal (start end)
f3a221cf 296 (remove-overlays start end 'hide-ifdef t)
722fa77f 297 (let ((o (make-overlay start end)))
f3a221cf
MR
298 (overlay-put o 'hide-ifdef t)
299 (if hide-ifdef-shadow
300 (overlay-put o 'face 'hide-ifdef-shadow)
301 (overlay-put o 'invisible 'hide-ifdef))))
722fa77f 302
175ce218
RS
303(defun hide-ifdef-region (start end)
304 "START is the start of a #if or #else form. END is the ending part.
305Everything including these lines is made invisible."
067d92a1 306 (save-excursion
722fa77f
SM
307 (goto-char start) (hif-end-of-line) (setq start (point))
308 (goto-char end) (hif-end-of-line) (setq end (point))
309 (hide-ifdef-region-internal start end)))
175ce218
RS
310
311(defun hif-show-ifdef-region (start end)
312 "Everything between START and END is made visible."
f3a221cf 313 (remove-overlays start end 'hide-ifdef t))
175ce218
RS
314
315
067d92a1 316;;===%%SF%% evaluation (Start) ===
175ce218 317
f5356416
RS
318;; It is not useful to set this to anything but `eval'.
319;; In fact, the variable might as well be eliminated.
175ce218 320(defvar hide-ifdef-evaluator 'eval
f5356416
RS
321 "The function to use to evaluate a form.
322The evaluator is given a canonical form and returns t if text under
175ce218
RS
323that form should be displayed.")
324
325(defvar hif-undefined-symbol nil
326 "...is by default considered to be false.")
327
175ce218
RS
328
329(defun hif-set-var (var value)
7644aa97 330 "Prepend (var value) pair to `hide-ifdef-env'."
175ce218
RS
331 (setq hide-ifdef-env (cons (cons var value) hide-ifdef-env)))
332
a96e1cb7
CY
333(declare-function semantic-c-hideif-lookup "semantic/bovine/c" (var))
334(declare-function semantic-c-hideif-defined "semantic/bovine/c" (var))
175ce218
RS
335
336(defun hif-lookup (var)
a96e1cb7
CY
337 (or (when (bound-and-true-p semantic-c-takeover-hideif)
338 (semantic-c-hideif-lookup var))
339 (let ((val (assoc var hide-ifdef-env)))
340 (if val
341 (cdr val)
342 hif-undefined-symbol))))
175ce218
RS
343
344(defun hif-defined (var)
a96e1cb7
CY
345 (cond
346 ((bound-and-true-p semantic-c-takeover-hideif)
347 (semantic-c-hideif-defined var))
348 ((assoc var hide-ifdef-env) 1)
349 (t 0)))
175ce218 350
067d92a1 351;;===%%SF%% evaluation (End) ===
175ce218
RS
352
353
354
067d92a1 355;;===%%SF%% parsing (Start) ===
175ce218
RS
356;;; The code that understands what ifs and ifdef in files look like.
357
358(defconst hif-cpp-prefix "\\(^\\|\r\\)[ \t]*#[ \t]*")
359(defconst hif-ifndef-regexp (concat hif-cpp-prefix "ifndef"))
360(defconst hif-ifx-regexp (concat hif-cpp-prefix "if\\(n?def\\)?[ \t]+"))
361(defconst hif-else-regexp (concat hif-cpp-prefix "else"))
362(defconst hif-endif-regexp (concat hif-cpp-prefix "endif"))
363(defconst hif-ifx-else-endif-regexp
364 (concat hif-ifx-regexp "\\|" hif-else-regexp "\\|" hif-endif-regexp))
365
067d92a1
SM
366;; Used to store the current token and the whole token list during parsing.
367;; Only bound dynamically.
4e391a67
AS
368(defvar hif-token)
369(defvar hif-token-list)
175ce218 370
d302e5cf 371(defconst hif-token-alist
a9128931
LL
372 '(("||" . hif-or)
373 ("&&" . hif-and)
d302e5cf 374 ("|" . hif-logior)
a9128931 375 ("^" . hif-logxor)
d302e5cf 376 ("&" . hif-logand)
a9128931
LL
377 ("<<" . hif-shiftleft)
378 (">>" . hif-shiftright)
379 ("==" . hif-equal)
380 ;; Note: we include tokens like `=' which aren't supported by CPP's
381 ;; expression syntax, because they are still relevant for the tokenizer,
382 ;; especially in conjunction with ##.
383 ("=" . hif-assign)
d302e5cf 384 ("!=" . hif-notequal)
a9128931
LL
385 ("##" . hif-token-concat)
386 ("!" . hif-not)
387 ("~" . hif-lognot)
388 ("(" . hif-lparen)
389 (")" . hif-rparen)
d302e5cf
SM
390 (">" . hif-greater)
391 ("<" . hif-less)
392 (">=" . hif-greater-equal)
393 ("<=" . hif-less-equal)
394 ("+" . hif-plus)
395 ("-" . hif-minus)
a9128931
LL
396 ("*" . hif-multiply)
397 ("/" . hif-divide)
398 ("%" . hif-modulo)
d302e5cf
SM
399 ("?" . hif-conditional)
400 (":" . hif-colon)))
401
722fa77f 402(defconst hif-token-regexp
a9128931
LL
403 (concat (regexp-opt (mapcar 'car hif-token-alist))
404 "\\|0x[0-9a-fA-F]+\\.?[0-9a-fA-F]*"
405 "\\|[0-9]+\\.?[0-9]*" ;; decimal/octal
406 "\\|\\w+"))
407
408(defconst hif-string-literal-regexp "\\(\"\\(?:[^\"\\]\\|\\\\.\\)*\"\\)")
409
45393801
JB
410(defun hif-string-to-number (string &optional base)
411 "Like `string-to-number', but it understands non-decimal floats."
412 (if (or (not base) (= base 10))
413 (string-to-number string base)
414 (let* ((parts (split-string string "\\." t "[ \t]+"))
da20e0f1
WX
415 (frac (cadr parts))
416 (quot (expt (* base 1.0) (length frac)))
417 (num (/ (string-to-number (concat (car parts) frac) base)
418 quot)))
419 (if (= num (truncate num))
420 (truncate num)
421 num))))
175ce218 422
722fa77f
SM
423(defun hif-tokenize (start end)
424 "Separate string between START and END into a list of tokens."
425 (let ((token-list nil))
067d92a1 426 (with-syntax-table hide-ifdef-syntax-table
722fa77f
SM
427 (save-excursion
428 (goto-char start)
429 (while (progn (forward-comment (point-max)) (< (point) end))
430 ;; (message "expr-start = %d" expr-start) (sit-for 1)
431 (cond
432 ((looking-at "\\\\\n")
433 (forward-char 2))
434
a9128931
LL
435 ((looking-at hif-string-literal-regexp)
436 (push (substring-no-properties (match-string 1)) token-list)
437 (goto-char (match-end 0)))
722fa77f
SM
438 ((looking-at hif-token-regexp)
439 (let ((token (buffer-substring (point) (match-end 0))))
440 (goto-char (match-end 0))
441 ;; (message "token: %s" token) (sit-for 1)
a9128931
LL
442 (push
443 (or (cdr (assoc token hif-token-alist))
444 (if (string-equal token "defined") 'hif-defined)
445 ;; TODO:
446 ;; 1. postfix 'l', 'll', 'ul' and 'ull'
447 ;; 2. floating number formats
45393801 448 ;; 3. 098 is interpreted as octal conversion error
a9128931
LL
449 (if (string-match "0x\\([0-9a-fA-F]+\\.?[0-9a-fA-F]*\\)"
450 token)
45393801 451 (hif-string-to-number (match-string 1 token) 16)) ;; hex
a9128931 452 (if (string-match "\\`0[0-9]+\\(\\.[0-9]+\\)?\\'" token)
45393801 453 (hif-string-to-number token 8)) ;; octal
a9128931
LL
454 (if (string-match "\\`[1-9][0-9]*\\(\\.[0-9]+\\)?\\'"
455 token)
456 (string-to-number token)) ;; decimal
457 (intern token))
458 token-list)))
722fa77f 459 (t (error "Bad #if expression: %s" (buffer-string)))))))
e945e87d 460 (nreverse token-list)))
175ce218 461
a9128931
LL
462;;------------------------------------------------------------------------
463;; Translate C preprocessor #if expressions using recursive descent.
464;; This parser was limited to the operators &&, ||, !, and "defined".
465;; Added ==, !=, +, and -. Gary Oberbrunner, garyo@avs.com, 8/9/94
466;;
467;; Implement the C language operator precedence table. Add all those
468;; missing operators that could be used in macros. Luke Lee 2013-09-04
469
470;; | Operator Type | Operator | Associativity |
471;; +----------------------+-----------------------------+---------------+
472;; | Primary Expression | () [] . -> expr++ expr-- | left-to-right |
473;; | Unary Operators | * & + - ! ~ ++expr --expr | right-to-left |
474;; | | (typecast) sizeof | |
475;; | Binary Operators | * / % | left-to-right |
476;; | | + - | |
477;; | | >> << | |
478;; | | < > <= >= | |
479;; | | == != | |
480;; | | & | |
481;; | | ^ | |
482;; | | | | |
483;; | | && | |
484;; | | || | |
485;; | Ternary Operator | ?: | right-to-left |
486;; x| Assignment Operators | = += -= *= /= %= >>= <<= &= | right-to-left |
487;; | | ^= = | |
488;; | Comma | , | left-to-right |
175ce218 489
f1259a53 490(defsubst hif-nexttoken ()
7644aa97 491 "Pop the next token from token-list into the let variable `hif-token'."
f1259a53
SM
492 (setq hif-token (pop hif-token-list)))
493
e02f48d7 494(defun hif-parse-if-exp (token-list)
175ce218 495 "Parse the TOKEN-LIST. Return translated list in prefix form."
e02f48d7
JB
496 (let ((hif-token-list token-list))
497 (hif-nexttoken)
498 (prog1
a9128931
LL
499 (and hif-token
500 (hif-exprlist))
e02f48d7
JB
501 (if hif-token ; is there still a token?
502 (error "Error: unexpected token: %s" hif-token)))))
175ce218 503
a9128931
LL
504(defun hif-exprlist ()
505 "Parse an exprlist: expr { ',' expr}"
506 (let ((result (hif-expr)))
507 (if (eq hif-token 'hif-comma)
508 (let ((temp (list result)))
509 (while
510 (progn
511 (hif-nexttoken)
512 (push (hif-expr) temp)
513 (eq hif-token 'hif-comma)))
514 (cons 'hif-comma (nreverse temp)))
515 result)))
516
175ce218 517(defun hif-expr ()
f5356416 518 "Parse an expression as found in #if.
2dc2ec3d
AS
519 expr : or-expr | or-expr '?' expr ':' expr."
520 (let ((result (hif-or-expr))
521 middle)
522 (while (eq hif-token 'hif-conditional)
523 (hif-nexttoken)
524 (setq middle (hif-expr))
525 (if (eq hif-token 'hif-colon)
526 (progn
527 (hif-nexttoken)
528 (setq result (list 'hif-conditional result middle (hif-expr))))
529 (error "Error: unexpected token: %s" hif-token)))
530 result))
531
532(defun hif-or-expr ()
a9128931 533 "Parse an or-expr : and-expr | or-expr '||' and-expr."
2dc2ec3d 534 (let ((result (hif-and-expr)))
a9128931 535 (while (eq hif-token 'hif-or)
175ce218 536 (hif-nexttoken)
2dc2ec3d 537 (setq result (list 'hif-or result (hif-and-expr))))
073c9531 538 result))
175ce218 539
2dc2ec3d 540(defun hif-and-expr ()
a9128931
LL
541 "Parse an and-expr : logior-expr | and-expr '&&' logior-expr."
542 (let ((result (hif-logior-expr)))
543 (while (eq hif-token 'hif-and)
544 (hif-nexttoken)
545 (setq result (list 'hif-and result (hif-logior-expr))))
546 result))
547
548(defun hif-logior-expr ()
549 "Parse a logor-expr : logxor-expr | logor-expr '|' logxor-expr."
550 (let ((result (hif-logxor-expr)))
551 (while (eq hif-token 'hif-logior)
552 (hif-nexttoken)
553 (setq result (list 'hif-logior result (hif-logxor-expr))))
554 result))
555
556(defun hif-logxor-expr ()
557 "Parse a logxor-expr : logand-expr | logxor-expr '^' logand-expr."
558 (let ((result (hif-logand-expr)))
559 (while (eq hif-token 'hif-logxor)
560 (hif-nexttoken)
561 (setq result (list 'hif-logxor result (hif-logand-expr))))
562 result))
563
564(defun hif-logand-expr ()
565 "Parse a logand-expr : eq-expr | logand-expr '&' eq-expr."
7bbe1dea 566 (let ((result (hif-eq-expr)))
a9128931 567 (while (eq hif-token 'hif-logand)
175ce218 568 (hif-nexttoken)
a9128931 569 (setq result (list 'hif-logand result (hif-eq-expr))))
073c9531 570 result))
175ce218 571
7bbe1dea 572(defun hif-eq-expr ()
a9128931
LL
573 "Parse an eq-expr : comp | eq-expr `=='|`!=' comp."
574 (let ((result (hif-comp-expr))
7bbe1dea 575 (eq-token nil))
a9128931 576 (while (memq hif-token '(hif-equal hif-notequal))
4e391a67 577 (setq eq-token hif-token)
7bbe1dea 578 (hif-nexttoken)
a9128931
LL
579 (setq result (list eq-token result (hif-comp-expr))))
580 result))
581
582(defun hif-comp-expr ()
583 "Parse a comp-expr : logshift | comp-expr `<'|`>'|`>='|`<=' logshift."
584 (let ((result (hif-logshift-expr))
585 (comp-token nil))
586 (while (memq hif-token '(hif-greater hif-less hif-greater-equal hif-less-equal))
587 (setq comp-token hif-token)
588 (hif-nexttoken)
589 (setq result (list comp-token result (hif-logshift-expr))))
590 result))
591
592(defun hif-logshift-expr ()
593 "Parse a logshift : math | logshift `<<'|`>>' math."
594 (let ((result (hif-math))
595 (shift-token nil))
596 (while (memq hif-token '(hif-shiftleft hif-shiftright))
597 (setq shift-token hif-token)
598 (hif-nexttoken)
599 (setq result (list shift-token result (hif-math))))
7bbe1dea
RS
600 result))
601
602(defun hif-math ()
a9128931
LL
603 "Parse an expression with + or -.
604 math : muldiv | math '+|-' muldiv."
605 (let ((result (hif-muldiv-expr))
606 (math-op nil))
607 (while (memq hif-token '(hif-plus hif-minus))
608 (setq math-op hif-token)
609 (hif-nexttoken)
610 (setq result (list math-op result (hif-muldiv-expr))))
611 result))
612
613(defun hif-muldiv-expr ()
614 "Parse an expression with *,/,%.
615 muldiv : factor | muldiv '*|/|%' factor."
7bbe1dea
RS
616 (let ((result (hif-factor))
617 (math-op nil))
a9128931 618 (while (memq hif-token '(hif-multiply hif-divide hif-modulo))
4e391a67 619 (setq math-op hif-token)
7bbe1dea
RS
620 (hif-nexttoken)
621 (setq result (list math-op result (hif-factor))))
622 result))
a1506d29 623
175ce218 624(defun hif-factor ()
a9128931 625 "Parse a factor: '!' factor | '~' factor | '(' expr ')' | 'defined(' id ')' | 'id(parmlist)' | strings | id."
175ce218 626 (cond
a9128931 627 ((eq hif-token 'hif-not)
d8f1319a 628 (hif-nexttoken)
722fa77f 629 (list 'hif-not (hif-factor)))
d8f1319a 630
a9128931 631 ((eq hif-token 'hif-lognot)
d8f1319a 632 (hif-nexttoken)
a9128931
LL
633 (list 'hif-lognot (hif-factor)))
634
635 ((eq hif-token 'hif-lparen)
636 (hif-nexttoken)
637 (let ((result (hif-exprlist)))
638 (if (not (eq hif-token 'hif-rparen))
d8f1319a 639 (error "Bad token in parenthesized expression: %s" hif-token)
175ce218 640 (hif-nexttoken)
d8f1319a
GM
641 result)))
642
643 ((eq hif-token 'hif-defined)
644 (hif-nexttoken)
a9128931 645 (let ((paren (when (eq hif-token 'hif-lparen) (hif-nexttoken) t))
722fa77f 646 (ident hif-token))
a9128931 647 (if (memq hif-token '(or and not hif-defined hif-lparen hif-rparen))
d8f1319a 648 (error "Error: unexpected token: %s" hif-token))
722fa77f
SM
649 (when paren
650 (hif-nexttoken)
a9128931 651 (unless (eq hif-token 'hif-rparen)
722fa77f 652 (error "Error: expected \")\" after identifier")))
d8f1319a 653 (hif-nexttoken)
067d92a1 654 `(hif-defined (quote ,ident))))
d8f1319a 655
722fa77f
SM
656 ((numberp hif-token)
657 (prog1 hif-token (hif-nexttoken)))
658
fb970f91
SM
659 ;; Unary plus/minus.
660 ((memq hif-token '(hif-minus hif-plus))
661 (list (prog1 hif-token (hif-nexttoken)) 0 (hif-factor)))
e02f48d7 662
d8f1319a
GM
663 (t ; identifier
664 (let ((ident hif-token))
665 (if (memq ident '(or and))
666 (error "Error: missing identifier"))
667 (hif-nexttoken)
067d92a1 668 `(hif-lookup (quote ,ident))))))
175ce218 669
7bbe1dea
RS
670(defun hif-mathify (val)
671 "Treat VAL as a number: if it's t or nil, use 1 or 0."
067d92a1
SM
672 (cond ((eq val t) 1)
673 ((null val) 0)
7bbe1dea
RS
674 (t val)))
675
2dc2ec3d
AS
676(defun hif-conditional (a b c)
677 (if (not (zerop (hif-mathify a))) (hif-mathify b) (hif-mathify c)))
722fa77f
SM
678(defun hif-and (a b)
679 (and (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
680(defun hif-or (a b)
681 (or (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
682(defun hif-not (a)
683 (zerop (hif-mathify a)))
a9128931
LL
684(defun hif-lognot (a)
685 (lognot (hif-mathify a)))
d302e5cf
SM
686
687(defmacro hif-mathify-binop (fun)
688 `(lambda (a b)
689 ,(format "Like `%s' but treat t and nil as 1 and 0." fun)
690 (,fun (hif-mathify a) (hif-mathify b))))
691
a9128931
LL
692(defun hif-shiftleft (a b)
693 (setq a (hif-mathify a))
694 (setq b (hif-mathify b))
695 (if (< a 0)
696 (ash a b)
697 (lsh a b)))
698
699(defun hif-shiftright (a b)
700 (setq a (hif-mathify a))
701 (setq b (hif-mathify b))
702 (if (< a 0)
703 (ash a (- b))
704 (lsh a (- b))))
705
706
707(defalias 'hif-multiply (hif-mathify-binop *))
708(defalias 'hif-divide (hif-mathify-binop /))
709(defalias 'hif-modulo (hif-mathify-binop %))
d302e5cf
SM
710(defalias 'hif-plus (hif-mathify-binop +))
711(defalias 'hif-minus (hif-mathify-binop -))
a9128931 712(defalias 'hif-equal (hif-mathify-binop =))
d302e5cf
SM
713(defalias 'hif-notequal (hif-mathify-binop /=))
714(defalias 'hif-greater (hif-mathify-binop >))
715(defalias 'hif-less (hif-mathify-binop <))
716(defalias 'hif-greater-equal (hif-mathify-binop >=))
717(defalias 'hif-less-equal (hif-mathify-binop <=))
718(defalias 'hif-logior (hif-mathify-binop logior))
a9128931 719(defalias 'hif-logxor (hif-mathify-binop logxor))
d302e5cf
SM
720(defalias 'hif-logand (hif-mathify-binop logand))
721
a9128931
LL
722
723(defun hif-comma (&rest expr)
7644aa97 724 "Evaluate a list of expr, return the result of the last item."
a9128931
LL
725 (let ((result nil))
726 (dolist (e expr)
727 (ignore-errors
728 (setq result (funcall hide-ifdef-evaluator e))))
729 result))
730
731
175ce218
RS
732;;;----------- end of parser -----------------------
733
734
735(defun hif-canonicalize ()
067d92a1 736 "When at beginning of #ifX, return a Lisp expression for its condition."
175ce218
RS
737 (save-excursion
738 (let ((negate (looking-at hif-ifndef-regexp)))
739 (re-search-forward hif-ifx-regexp)
722fa77f
SM
740 (let* ((tokens (hif-tokenize (point)
741 (progn (hif-end-of-line) (point))))
742 (expr (hif-parse-if-exp tokens)))
067d92a1 743 ;; (message "hif-canonicalized: %s" expr)
175ce218 744 (if negate
722fa77f 745 (list 'hif-not expr)
175ce218
RS
746 expr)))))
747
748
749(defun hif-find-any-ifX ()
f5356416 750 "Move to next #if..., or #ifndef, at point or after."
067d92a1 751 ;; (message "find ifX at %d" (point))
175ce218
RS
752 (prog1
753 (re-search-forward hif-ifx-regexp (point-max) t)
754 (beginning-of-line)))
755
756
757(defun hif-find-next-relevant ()
f5356416 758 "Move to next #if..., #else, or #endif, after the current line."
067d92a1 759 ;; (message "hif-find-next-relevant at %d" (point))
175ce218 760 (end-of-line)
067d92a1 761 ;; avoid infinite recursion by only going to beginning of line if match found
175ce218 762 (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
073c9531 763 (beginning-of-line)))
175ce218
RS
764
765(defun hif-find-previous-relevant ()
f5356416 766 "Move to previous #if..., #else, or #endif, before the current line."
067d92a1 767 ;; (message "hif-find-previous-relevant at %d" (point))
175ce218 768 (beginning-of-line)
067d92a1 769 ;; avoid infinite recursion by only going to beginning of line if match found
175ce218 770 (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
073c9531 771 (beginning-of-line)))
175ce218
RS
772
773
774(defun hif-looking-at-ifX () ;; Should eventually see #if
775 (looking-at hif-ifx-regexp))
776(defun hif-looking-at-endif ()
777 (looking-at hif-endif-regexp))
778(defun hif-looking-at-else ()
779 (looking-at hif-else-regexp))
780
781
782
783(defun hif-ifdef-to-endif ()
784 "If positioned at #ifX or #else form, skip to corresponding #endif."
067d92a1 785 ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
175ce218
RS
786 (hif-find-next-relevant)
787 (cond ((hif-looking-at-ifX)
788 (hif-ifdef-to-endif) ; find endif of nested if
789 (hif-ifdef-to-endif)) ; find outer endif or else
790 ((hif-looking-at-else)
791 (hif-ifdef-to-endif)) ; find endif following else
792 ((hif-looking-at-endif)
793 'done)
794 (t
eb8c3be9 795 (error "Mismatched #ifdef #endif pair"))))
175ce218
RS
796
797
798(defun hif-endif-to-ifdef ()
799 "If positioned at #endif form, skip backward to corresponding #ifX."
067d92a1 800 ;; (message "hif-endif-to-ifdef at %d" (point))
175ce218
RS
801 (let ((start (point)))
802 (hif-find-previous-relevant)
803 (if (= start (point))
eb8c3be9 804 (error "Mismatched #ifdef #endif pair")))
175ce218
RS
805 (cond ((hif-looking-at-endif)
806 (hif-endif-to-ifdef) ; find beginning of nested if
807 (hif-endif-to-ifdef)) ; find beginning of outer if or else
808 ((hif-looking-at-else)
809 (hif-endif-to-ifdef))
810 ((hif-looking-at-ifX)
811 'done)
0b030df7 812 (t))) ; never gets here
175ce218
RS
813
814
815(defun forward-ifdef (&optional arg)
816 "Move point to beginning of line of the next ifdef-endif.
073c9531 817With argument, do this that many times."
175ce218
RS
818 (interactive "p")
819 (or arg (setq arg 1))
067d92a1
SM
820 (if (< arg 0) (backward-ifdef (- arg))
821 (while (< 0 arg)
822 (setq arg (- arg))
823 (let ((start (point)))
824 (unless (hif-looking-at-ifX)
175ce218 825 (hif-find-next-relevant))
067d92a1
SM
826 (if (hif-looking-at-ifX)
827 (hif-ifdef-to-endif)
828 (goto-char start)
829 (error "No following #ifdef"))))))
175ce218
RS
830
831
832(defun backward-ifdef (&optional arg)
833 "Move point to beginning of the previous ifdef-endif.
073c9531 834With argument, do this that many times."
175ce218
RS
835 (interactive "p")
836 (or arg (setq arg 1))
067d92a1
SM
837 (if (< arg 0) (forward-ifdef (- arg))
838 (while (< 0 arg)
839 (setq arg (1- arg))
840 (beginning-of-line)
841 (let ((start (point)))
842 (unless (hif-looking-at-endif)
175ce218 843 (hif-find-previous-relevant))
067d92a1
SM
844 (if (hif-looking-at-endif)
845 (hif-endif-to-ifdef)
846 (goto-char start)
847 (error "No previous #ifdef"))))))
175ce218
RS
848
849
850(defun down-ifdef ()
851 "Move point to beginning of nested ifdef or else-part."
852 (interactive)
853 (let ((start (point)))
854 (hif-find-next-relevant)
855 (if (or (hif-looking-at-ifX) (hif-looking-at-else))
856 ()
857 (goto-char start)
073c9531 858 (error "No following #ifdef"))))
175ce218
RS
859
860
861(defun up-ifdef ()
862 "Move point to beginning of enclosing ifdef or else-part."
863 (interactive)
864 (beginning-of-line)
865 (let ((start (point)))
067d92a1
SM
866 (unless (hif-looking-at-endif)
867 (hif-find-previous-relevant))
175ce218
RS
868 (if (hif-looking-at-endif)
869 (hif-endif-to-ifdef))
870 (if (= start (point))
073c9531 871 (error "No previous #ifdef"))))
175ce218
RS
872
873(defun next-ifdef (&optional arg)
874 "Move to the beginning of the next #ifX, #else, or #endif.
073c9531 875With argument, do this that many times."
175ce218
RS
876 (interactive "p")
877 (or arg (setq arg 1))
067d92a1
SM
878 (if (< arg 0) (previous-ifdef (- arg))
879 (while (< 0 arg)
880 (setq arg (1- arg))
881 (hif-find-next-relevant)
882 (when (eolp)
883 (beginning-of-line)
884 (error "No following #ifdefs, #elses, or #endifs")))))
175ce218
RS
885
886(defun previous-ifdef (&optional arg)
887 "Move to the beginning of the previous #ifX, #else, or #endif.
073c9531 888With argument, do this that many times."
175ce218
RS
889 (interactive "p")
890 (or arg (setq arg 1))
067d92a1
SM
891 (if (< arg 0) (next-ifdef (- arg))
892 (while (< 0 arg)
893 (setq arg (1- arg))
894 (let ((start (point)))
895 (hif-find-previous-relevant)
896 (if (= start (point))
897 (error "No previous #ifdefs, #elses, or #endifs"))))))
175ce218
RS
898
899
067d92a1 900;;===%%SF%% parsing (End) ===
175ce218
RS
901
902
067d92a1 903;;===%%SF%% hide-ifdef-hiding (Start) ===
175ce218
RS
904
905
906;;; A range is a structure with four components:
907;;; ELSE-P True if there was an else clause for the ifdef.
908;;; START The start of the range. (beginning of line)
909;;; ELSE The else marker (beginning of line)
910;;; Only valid if ELSE-P is true.
911;;; END The end of the range. (beginning of line)
912
722fa77f 913(defsubst hif-make-range (start end &optional else)
067d92a1 914 (list start else end))
175ce218 915
722fa77f
SM
916(defsubst hif-range-start (range) (elt range 0))
917(defsubst hif-range-else (range) (elt range 1))
918(defsubst hif-range-end (range) (elt range 2))
175ce218
RS
919
920
921
922;;; Find-Range
923;;; The workhorse, it delimits the #if region. Reasonably simple:
924;;; Skip until an #else or #endif is found, remembering positions. If
925;;; an #else was found, skip some more, looking for the true #endif.
926
927(defun hif-find-range ()
067d92a1 928 "Return a Range structure describing the current #if region.
175ce218 929Point is left unchanged."
067d92a1 930 ;; (message "hif-find-range at %d" (point))
175ce218
RS
931 (save-excursion
932 (beginning-of-line)
933 (let ((start (point))
175ce218
RS
934 (else nil)
935 (end nil))
936 ;; Part one. Look for either #endif or #else.
937 ;; This loop-and-a-half dedicated to E. Dijkstra.
067d92a1
SM
938 (while (progn
939 (hif-find-next-relevant)
940 (hif-looking-at-ifX)) ; Skip nested ifdef
941 (hif-ifdef-to-endif))
175ce218
RS
942 ;; Found either a #else or an #endif.
943 (cond ((hif-looking-at-else)
175ce218
RS
944 (setq else (point)))
945 (t
5ed619e0 946 (setq end (point)))) ; (line-end-position)
175ce218 947 ;; If found #else, look for #endif.
067d92a1
SM
948 (when else
949 (while (progn
950 (hif-find-next-relevant)
951 (hif-looking-at-ifX)) ; Skip nested ifdef
952 (hif-ifdef-to-endif))
953 (if (hif-looking-at-else)
954 (error "Found two elses in a row? Broken!"))
5ed619e0 955 (setq end (point))) ; (line-end-position)
067d92a1 956 (hif-make-range start end else))))
175ce218 957
a1506d29 958
175ce218 959;;; A bit slimy.
175ce218
RS
960
961(defun hif-hide-line (point)
073c9531 962 "Hide the line containing point. Does nothing if `hide-ifdef-lines' is nil."
f3a221cf
MR
963 (when hide-ifdef-lines
964 (save-excursion
965 (goto-char point)
966 (hide-ifdef-region-internal
967 (line-beginning-position) (progn (hif-end-of-line) (point))))))
a1506d29 968
175ce218
RS
969
970;;; Hif-Possibly-Hide
971;;; There are four cases. The #ifX expression is "taken" if it
972;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
973;;; inside the #ifdef would be included when the program was
a1506d29 974;;; compiled.
175ce218
RS
975;;;
976;;; Case 1: #ifX taken, and there's an #else.
977;;; The #else part must be hidden. The #if (then) part must be
978;;; processed for nested #ifX's.
979;;; Case 2: #ifX taken, and there's no #else.
980;;; The #if part must be processed for nested #ifX's.
981;;; Case 3: #ifX not taken, and there's an #else.
982;;; The #if part must be hidden. The #else part must be processed
983;;; for nested #ifs.
984;;; Case 4: #ifX not taken, and there's no #else.
985;;; The #ifX part must be hidden.
986;;;
987;;; Further processing is done by narrowing to the relevant region
988;;; and just recursively calling hide-ifdef-guts.
989;;;
990;;; When hif-possibly-hide returns, point is at the end of the
991;;; possibly-hidden range.
992
993(defun hif-recurse-on (start end)
073c9531 994 "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
175ce218
RS
995 (save-excursion
996 (save-restriction
997 (goto-char start)
998 (end-of-line)
999 (narrow-to-region (point) end)
1000 (hide-ifdef-guts))))
1001
1002(defun hif-possibly-hide ()
f5356416 1003 "Called at #ifX expression, this hides those parts that should be hidden.
333f9019 1004It uses the judgment of `hide-ifdef-evaluator'."
067d92a1
SM
1005 ;; (message "hif-possibly-hide") (sit-for 1)
1006 (let ((test (hif-canonicalize))
1007 (range (hif-find-range)))
1008 ;; (message "test = %s" test) (sit-for 1)
a1506d29 1009
067d92a1 1010 (hif-hide-line (hif-range-end range))
722fa77f
SM
1011 (if (not (hif-not (funcall hide-ifdef-evaluator test)))
1012 (cond ((hif-range-else range) ; case 1
175ce218 1013 (hif-hide-line (hif-range-else range))
722fa77f 1014 (hide-ifdef-region (hif-range-else range)
067d92a1
SM
1015 (1- (hif-range-end range)))
1016 (hif-recurse-on (hif-range-start range)
1017 (hif-range-else range)))
1018 (t ; case 2
1019 (hif-recurse-on (hif-range-start range)
1020 (hif-range-end range))))
1021 (cond ((hif-range-else range) ; case 3
1022 (hif-hide-line (hif-range-else range))
1023 (hide-ifdef-region (hif-range-start range)
1024 (1- (hif-range-else range)))
1025 (hif-recurse-on (hif-range-else range)
1026 (hif-range-end range)))
1027 (t ; case 4
1028 (hide-ifdef-region (point)
1029 (1- (hif-range-end range))))))
1030 (hif-hide-line (hif-range-start range)) ; Always hide start.
1031 (goto-char (hif-range-end range))
1032 (end-of-line)))
175ce218
RS
1033
1034
1035
1036(defun hide-ifdef-guts ()
f5356416
RS
1037 "Does most of the work of `hide-ifdefs'.
1038It does not do the work that's pointless to redo on a recursive entry."
067d92a1 1039 ;; (message "hide-ifdef-guts")
175ce218
RS
1040 (save-excursion
1041 (goto-char (point-min))
1042 (while (hif-find-any-ifX)
1043 (hif-possibly-hide))))
1044
067d92a1 1045;;===%%SF%% hide-ifdef-hiding (End) ===
175ce218
RS
1046
1047
067d92a1 1048;;===%%SF%% exports (Start) ===
175ce218 1049
175ce218 1050(defun hide-ifdef-toggle-read-only ()
067d92a1 1051 "Toggle `hide-ifdef-read-only'."
175ce218
RS
1052 (interactive)
1053 (setq hide-ifdef-read-only (not hide-ifdef-read-only))
1054 (message "Hide-Read-Only %s"
1055 (if hide-ifdef-read-only "ON" "OFF"))
1056 (if hide-ifdef-hiding
1057 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
c63e0f9a 1058 (force-mode-line-update))
175ce218
RS
1059
1060(defun hide-ifdef-toggle-outside-read-only ()
f5356416 1061 "Replacement for `toggle-read-only' within Hide-Ifdef mode."
175ce218
RS
1062 (interactive)
1063 (setq hif-outside-read-only (not hif-outside-read-only))
1064 (message "Read only %s"
1065 (if hif-outside-read-only "ON" "OFF"))
1066 (setq buffer-read-only
1067 (or (and hide-ifdef-hiding hide-ifdef-read-only)
067d92a1 1068 hif-outside-read-only))
c63e0f9a 1069 (force-mode-line-update))
175ce218 1070
f3a221cf
MR
1071(defun hide-ifdef-toggle-shadowing ()
1072 "Toggle shadowing."
1073 (interactive)
1074 (set (make-local-variable 'hide-ifdef-shadow) (not hide-ifdef-shadow))
1075 (message "Shadowing %s" (if hide-ifdef-shadow "ON" "OFF"))
1076 (save-restriction
1077 (widen)
1078 (dolist (overlay (overlays-in (point-min) (point-max)))
1079 (when (overlay-get overlay 'hide-ifdef)
1080 (if hide-ifdef-shadow
1081 (progn
1082 (overlay-put overlay 'invisible nil)
1083 (overlay-put overlay 'face 'hide-ifdef-shadow))
1084 (overlay-put overlay 'face nil)
1085 (overlay-put overlay 'invisible 'hide-ifdef))))))
a1506d29 1086
175ce218
RS
1087(defun hide-ifdef-define (var)
1088 "Define a VAR so that #ifdef VAR would be included."
1089 (interactive "SDefine what? ")
7bbe1dea 1090 (hif-set-var var 1)
175ce218
RS
1091 (if hide-ifdef-hiding (hide-ifdefs)))
1092
1093(defun hide-ifdef-undef (var)
1094 "Undefine a VAR so that #ifdef VAR would not be included."
1095 (interactive "SUndefine what? ")
1096 (hif-set-var var nil)
1097 (if hide-ifdef-hiding (hide-ifdefs)))
1098
1099
f5356416 1100(defun hide-ifdefs (&optional nomsg)
a1506d29
JB
1101 "Hide the contents of some #ifdefs.
1102Assume that defined symbols have been added to `hide-ifdef-env'.
579e495a
CZ
1103The text hidden is the text that would not be included by the C
1104preprocessor if it were given the file with those symbols defined.
175ce218 1105
d1fa6aff 1106Turn off hiding by calling `show-ifdefs'."
175ce218
RS
1107
1108 (interactive)
1109 (message "Hiding...")
3edc14ae 1110 (setq hif-outside-read-only buffer-read-only)
067d92a1 1111 (unless hide-ifdef-mode (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
175ce218
RS
1112 (if hide-ifdef-hiding
1113 (show-ifdefs)) ; Otherwise, deep confusion.
067d92a1
SM
1114 (setq hide-ifdef-hiding t)
1115 (hide-ifdef-guts)
36f063e8 1116 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only))
f5356416
RS
1117 (or nomsg
1118 (message "Hiding done")))
175ce218
RS
1119
1120
1121(defun show-ifdefs ()
f5356416 1122 "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
175ce218 1123 (interactive)
36f063e8 1124 (setq buffer-read-only hif-outside-read-only)
067d92a1 1125 (hif-show-all)
073c9531 1126 (setq hide-ifdef-hiding nil))
175ce218
RS
1127
1128
1129(defun hif-find-ifdef-block ()
7644aa97 1130 "Utility to hide and show ifdef block.
4e391a67 1131Return as (TOP . BOTTOM) the extent of ifdef block."
175ce218 1132 (let (max-bottom)
4e391a67
AS
1133 (cons (save-excursion
1134 (beginning-of-line)
067d92a1
SM
1135 (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
1136 (up-ifdef))
4e391a67
AS
1137 (prog1 (point)
1138 (hif-ifdef-to-endif)
1139 (setq max-bottom (1- (point)))))
1140 (save-excursion
1141 (beginning-of-line)
067d92a1
SM
1142 (unless (hif-looking-at-endif)
1143 (hif-find-next-relevant))
4e391a67
AS
1144 (while (hif-looking-at-ifX)
1145 (hif-ifdef-to-endif)
1146 (hif-find-next-relevant))
1147 (min max-bottom (1- (point)))))))
175ce218
RS
1148
1149
1150(defun hide-ifdef-block ()
1151 "Hide the ifdef block (true or false part) enclosing or before the cursor."
1152 (interactive)
067d92a1
SM
1153 (unless hide-ifdef-mode (hide-ifdef-mode 1))
1154 (let ((top-bottom (hif-find-ifdef-block)))
4e391a67 1155 (hide-ifdef-region (car top-bottom) (cdr top-bottom))
067d92a1
SM
1156 (when hide-ifdef-lines
1157 (hif-hide-line (car top-bottom))
1158 (hif-hide-line (1+ (cdr top-bottom))))
073c9531 1159 (setq hide-ifdef-hiding t))
36f063e8 1160 (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
175ce218 1161
175ce218
RS
1162(defun show-ifdef-block ()
1163 "Show the ifdef block (true or false part) enclosing or before the cursor."
1164 (interactive)
d689858f
EZ
1165 (let ((top-bottom (hif-find-ifdef-block)))
1166 (if hide-ifdef-lines
1167 (hif-show-ifdef-region
1168 (save-excursion
1169 (goto-char (car top-bottom)) (line-beginning-position))
1170 (save-excursion
1171 (goto-char (1+ (cdr top-bottom)))
1172 (hif-end-of-line) (point)))
067d92a1 1173 (hif-show-ifdef-region (1- (car top-bottom)) (cdr top-bottom)))))
175ce218
RS
1174
1175
eb8c3be9 1176;;; definition alist support
175ce218
RS
1177
1178(defvar hide-ifdef-define-alist nil
067d92a1 1179 "A global assoc list of pre-defined symbol lists.")
175ce218
RS
1180
1181(defun hif-compress-define-list (env)
1182 "Compress the define list ENV into a list of defined symbols only."
067d92a1
SM
1183 (let ((new-defs nil))
1184 (dolist (def env new-defs)
ffe6eaf1 1185 (if (hif-lookup (car def)) (push (car def) new-defs)))))
175ce218
RS
1186
1187(defun hide-ifdef-set-define-alist (name)
579e495a 1188 "Set the association for NAME to `hide-ifdef-env'."
175ce218 1189 (interactive "SSet define list: ")
067d92a1
SM
1190 (push (cons name (hif-compress-define-list hide-ifdef-env))
1191 hide-ifdef-define-alist))
175ce218
RS
1192
1193(defun hide-ifdef-use-define-alist (name)
579e495a 1194 "Set `hide-ifdef-env' to the define list specified by NAME."
48d66f99
KS
1195 (interactive
1196 (list (completing-read "Use define list: "
521b2748
SM
1197 (mapcar (lambda (x) (symbol-name (car x)))
1198 hide-ifdef-define-alist)
1199 nil t)))
48d66f99 1200 (if (stringp name) (setq name (intern name)))
175ce218
RS
1201 (let ((define-list (assoc name hide-ifdef-define-alist)))
1202 (if define-list
1203 (setq hide-ifdef-env
16fdbdce 1204 (mapcar (lambda (arg) (cons arg t))
175ce218
RS
1205 (cdr define-list)))
1206 (error "No define list for %s" name))
073c9531 1207 (if hide-ifdef-hiding (hide-ifdefs))))
175ce218 1208
048fb1b7
RS
1209(provide 'hideif)
1210
1a06eabd 1211;;; hideif.el ends here