hideif.el 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. ;;; hideif.el --- hides selected code within ifdef
  2. ;; Copyright (C) 1988, 1994, 2001-2012 Free Software Foundation, Inc.
  3. ;; Author: Brian Marick
  4. ;; Daniel LaLiberte <liberte@holonexus.org>
  5. ;; Maintainer: FSF
  6. ;; Keywords: c, outlines
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; To initialize, toggle the hide-ifdef minor mode with
  20. ;;
  21. ;; M-x hide-ifdef-mode
  22. ;;
  23. ;; This will set up key bindings and call hide-ifdef-mode-hook if it
  24. ;; has a value. To explicitly hide ifdefs using a buffer-local
  25. ;; define list (default empty), type
  26. ;;
  27. ;; M-x hide-ifdefs or C-c @ h
  28. ;;
  29. ;; Hide-ifdef suppresses the display of code that the preprocessor wouldn't
  30. ;; pass through. The support of constant expressions in #if lines is
  31. ;; limited to identifiers, parens, and the operators: &&, ||, !, and
  32. ;; "defined". Please extend this.
  33. ;;
  34. ;; The hidden code is marked by ellipses (...). Be
  35. ;; cautious when editing near ellipses, since the hidden text is
  36. ;; still in the buffer, and you can move the point into it and modify
  37. ;; text unawares.
  38. ;; You can make your buffer read-only while hide-ifdef-hiding by setting
  39. ;; hide-ifdef-read-only to a non-nil value. You can toggle this
  40. ;; variable with hide-ifdef-toggle-read-only (C-c @ C-q).
  41. ;;
  42. ;; You can undo the effect of hide-ifdefs by typing
  43. ;;
  44. ;; M-x show-ifdefs or C-c @ s
  45. ;;
  46. ;; Use M-x hide-ifdef-define (C-c @ d) to define a symbol.
  47. ;; Use M-x hide-ifdef-undef (C-c @ u) to undefine a symbol.
  48. ;;
  49. ;; If you define or undefine a symbol while hide-ifdef-mode is in effect,
  50. ;; the display will be updated. Only the define list for the current
  51. ;; buffer will be affected. You can save changes to the local define
  52. ;; list with hide-ifdef-set-define-alist. This adds entries
  53. ;; to hide-ifdef-define-alist.
  54. ;;
  55. ;; If you have defined a hide-ifdef-mode-hook, you can set
  56. ;; up a list of symbols that may be used by hide-ifdefs as in the
  57. ;; following example:
  58. ;;
  59. ;; (add-hook 'hide-ifdef-mode-hook
  60. ;; (lambda ()
  61. ;; (unless hide-ifdef-define-alist
  62. ;; (setq hide-ifdef-define-alist
  63. ;; '((list1 ONE TWO)
  64. ;; (list2 TWO THREE))))
  65. ;; (hide-ifdef-use-define-alist 'list2))) ; use list2 by default
  66. ;;
  67. ;; You can call hide-ifdef-use-define-alist (C-c @ U) at any time to specify
  68. ;; another list to use.
  69. ;;
  70. ;; To cause ifdefs to be hidden as soon as hide-ifdef-mode is called,
  71. ;; set hide-ifdef-initially to non-nil.
  72. ;;
  73. ;; If you set hide-ifdef-lines to t, hide-ifdefs hides all the #ifdef lines.
  74. ;; In the absence of highlighting, that might be a bad idea. If you set
  75. ;; hide-ifdef-lines to nil (the default), the surrounding preprocessor
  76. ;; lines will be displayed. That can be confusing in its own
  77. ;; right. Other variations on display are possible, but not much
  78. ;; better.
  79. ;;
  80. ;; You can explicitly hide or show individual ifdef blocks irrespective
  81. ;; of the define list by using hide-ifdef-block and show-ifdef-block.
  82. ;;
  83. ;; You can move the point between ifdefs with forward-ifdef, backward-ifdef,
  84. ;; up-ifdef, down-ifdef, next-ifdef, and previous-ifdef.
  85. ;;
  86. ;; If you have minor-mode-alist in your mode line (the default) two labels
  87. ;; may appear. "Ifdef" will appear when hide-ifdef-mode is active. "Hiding"
  88. ;; will appear when text may be hidden ("hide-ifdef-hiding" is non-nil).
  89. ;;
  90. ;; Written by Brian Marick, at Gould, Computer Systems Division, Urbana IL.
  91. ;; Extensively modified by Daniel LaLiberte (while at Gould).
  92. ;;; Code:
  93. (require 'cc-mode)
  94. (defgroup hide-ifdef nil
  95. "Hide selected code within `ifdef'."
  96. :group 'c)
  97. (defcustom hide-ifdef-initially nil
  98. "Non-nil means call `hide-ifdefs' when Hide-Ifdef mode is first activated."
  99. :type 'boolean
  100. :group 'hide-ifdef)
  101. (defcustom hide-ifdef-read-only nil
  102. "Set to non-nil if you want buffer to be read-only while hiding text."
  103. :type 'boolean
  104. :group 'hide-ifdef)
  105. (defcustom hide-ifdef-lines nil
  106. "Non-nil means hide the #ifX, #else, and #endif lines."
  107. :type 'boolean
  108. :group 'hide-ifdef)
  109. (defcustom hide-ifdef-shadow nil
  110. "Non-nil means shadow text instead of hiding it."
  111. :type 'boolean
  112. :group 'hide-ifdef
  113. :version "23.1")
  114. (defface hide-ifdef-shadow '((t (:inherit shadow)))
  115. "Face for shadowing ifdef blocks."
  116. :group 'hide-ifdef
  117. :version "23.1")
  118. (defvar hide-ifdef-mode-submap
  119. ;; Set up the submap that goes after the prefix key.
  120. (let ((map (make-sparse-keymap)))
  121. (define-key map "d" 'hide-ifdef-define)
  122. (define-key map "u" 'hide-ifdef-undef)
  123. (define-key map "D" 'hide-ifdef-set-define-alist)
  124. (define-key map "U" 'hide-ifdef-use-define-alist)
  125. (define-key map "h" 'hide-ifdefs)
  126. (define-key map "s" 'show-ifdefs)
  127. (define-key map "\C-d" 'hide-ifdef-block)
  128. (define-key map "\C-s" 'show-ifdef-block)
  129. (define-key map "\C-q" 'hide-ifdef-toggle-read-only)
  130. (define-key map "\C-w" 'hide-ifdef-toggle-shadowing)
  131. (substitute-key-definition
  132. 'toggle-read-only 'hide-ifdef-toggle-outside-read-only map)
  133. map)
  134. "Keymap used by `hide-ifdef-mode' under `hide-ifdef-mode-prefix-key'.")
  135. (defconst hide-ifdef-mode-prefix-key "\C-c@"
  136. "Prefix key for all Hide-Ifdef mode commands.")
  137. (defvar hide-ifdef-mode-map
  138. ;; Set up the mode's main map, which leads via the prefix key to the submap.
  139. (let ((map (make-sparse-keymap)))
  140. (define-key map hide-ifdef-mode-prefix-key hide-ifdef-mode-submap)
  141. map)
  142. "Keymap used with `hide-ifdef-mode'.")
  143. (easy-menu-define hide-ifdef-mode-menu hide-ifdef-mode-map
  144. "Menu for `hide-ifdef-mode'."
  145. '("Hide-Ifdef"
  146. ["Hide some ifdefs" hide-ifdefs
  147. :help "Hide the contents of some #ifdefs"]
  148. ["Show all ifdefs" show-ifdefs
  149. :help "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs"]
  150. ["Hide ifdef block" hide-ifdef-block
  151. :help "Hide the ifdef block (true or false part) enclosing or before the cursor"]
  152. ["Show ifdef block" show-ifdef-block
  153. :help "Show the ifdef block (true or false part) enclosing or before the cursor"]
  154. ["Define a variable..." hide-ifdef-define
  155. :help "Define a VAR so that #ifdef VAR would be included"]
  156. ["Undefine a variable..." hide-ifdef-undef
  157. :help "Undefine a VAR so that #ifdef VAR would not be included"]
  158. ["Define an alist..." hide-ifdef-set-define-alist
  159. :help "Set the association for NAME to `hide-ifdef-env'"]
  160. ["Use an alist..." hide-ifdef-use-define-alist
  161. :help "Set `hide-ifdef-env' to the define list specified by NAME"]
  162. ["Toggle read only" hide-ifdef-toggle-read-only
  163. :style toggle :selected hide-ifdef-read-only
  164. :help "Buffer should be read-only while hiding text"]
  165. ["Toggle shadowing" hide-ifdef-toggle-shadowing
  166. :style toggle :selected hide-ifdef-shadow
  167. :help "Text should be shadowed instead of hidden"]))
  168. (defvar hide-ifdef-hiding nil
  169. "Non-nil when text may be hidden.")
  170. (or (assq 'hide-ifdef-hiding minor-mode-alist)
  171. (setq minor-mode-alist
  172. (cons '(hide-ifdef-hiding " Hiding")
  173. minor-mode-alist)))
  174. ;; fix c-mode syntax table so we can recognize whole symbols.
  175. (defvar hide-ifdef-syntax-table
  176. (let ((st (copy-syntax-table c-mode-syntax-table)))
  177. (modify-syntax-entry ?_ "w" st)
  178. (modify-syntax-entry ?& "." st)
  179. (modify-syntax-entry ?\| "." st)
  180. st)
  181. "Syntax table used for tokenizing #if expressions.")
  182. (defvar hide-ifdef-env nil
  183. "An alist of defined symbols and their values.")
  184. (defvar hif-outside-read-only nil
  185. "Internal variable. Saves the value of `buffer-read-only' while hiding.")
  186. ;;;###autoload
  187. (define-minor-mode hide-ifdef-mode
  188. "Toggle features to hide/show #ifdef blocks (Hide-Ifdef mode).
  189. With a prefix argument ARG, enable Hide-Ifdef mode if ARG is
  190. positive, and disable it otherwise. If called from Lisp, enable
  191. the mode if ARG is omitted or nil.
  192. Hide-Ifdef mode is a buffer-local minor mode for use with C and
  193. C-like major modes. When enabled, code within #ifdef constructs
  194. that the C preprocessor would eliminate may be hidden from view.
  195. Several variables affect how the hiding is done:
  196. `hide-ifdef-env'
  197. An association list of defined and undefined symbols for the
  198. current buffer. Initially, the global value of `hide-ifdef-env'
  199. is used.
  200. `hide-ifdef-define-alist'
  201. An association list of defined symbol lists.
  202. Use `hide-ifdef-set-define-alist' to save the current `hide-ifdef-env'
  203. and `hide-ifdef-use-define-alist' to set the current `hide-ifdef-env'
  204. from one of the lists in `hide-ifdef-define-alist'.
  205. `hide-ifdef-lines'
  206. Set to non-nil to not show #if, #ifdef, #ifndef, #else, and
  207. #endif lines when hiding.
  208. `hide-ifdef-initially'
  209. Indicates whether `hide-ifdefs' should be called when Hide-Ifdef mode
  210. is activated.
  211. `hide-ifdef-read-only'
  212. Set to non-nil if you want to make buffers read only while hiding.
  213. After `show-ifdefs', read-only status is restored to previous value.
  214. \\{hide-ifdef-mode-map}"
  215. :group 'hide-ifdef :lighter " Ifdef"
  216. (if hide-ifdef-mode
  217. (progn
  218. ;; inherit global values
  219. (set (make-local-variable 'hide-ifdef-env)
  220. (default-value 'hide-ifdef-env))
  221. (set (make-local-variable 'hide-ifdef-hiding)
  222. (default-value 'hide-ifdef-hiding))
  223. (set (make-local-variable 'hif-outside-read-only) buffer-read-only)
  224. (set (make-local-variable 'line-move-ignore-invisible) t)
  225. (add-hook 'change-major-mode-hook
  226. (lambda () (hide-ifdef-mode -1)) nil t)
  227. (add-to-invisibility-spec '(hide-ifdef . t))
  228. (if hide-ifdef-initially
  229. (hide-ifdefs)
  230. (show-ifdefs)))
  231. ;; else end hide-ifdef-mode
  232. (kill-local-variable 'line-move-ignore-invisible)
  233. (remove-from-invisibility-spec '(hide-ifdef . t))
  234. (when hide-ifdef-hiding
  235. (show-ifdefs))))
  236. (defun hif-show-all ()
  237. "Show all of the text in the current buffer."
  238. (interactive)
  239. (hif-show-ifdef-region (point-min) (point-max)))
  240. ;; By putting this on after-revert-hook, we arrange that it only
  241. ;; does anything when revert-buffer avoids turning off the mode.
  242. ;; (That can happen in VC.)
  243. (defun hif-after-revert-function ()
  244. (and hide-ifdef-mode hide-ifdef-hiding
  245. (hide-ifdefs t)))
  246. (add-hook 'after-revert-hook 'hif-after-revert-function)
  247. (defun hif-end-of-line ()
  248. (end-of-line)
  249. (while (= (logand 1 (skip-chars-backward "\\\\")) 1)
  250. (end-of-line 2)))
  251. (defun hide-ifdef-region-internal (start end)
  252. (remove-overlays start end 'hide-ifdef t)
  253. (let ((o (make-overlay start end)))
  254. (overlay-put o 'hide-ifdef t)
  255. (if hide-ifdef-shadow
  256. (overlay-put o 'face 'hide-ifdef-shadow)
  257. (overlay-put o 'invisible 'hide-ifdef))))
  258. (defun hide-ifdef-region (start end)
  259. "START is the start of a #if or #else form. END is the ending part.
  260. Everything including these lines is made invisible."
  261. (save-excursion
  262. (goto-char start) (hif-end-of-line) (setq start (point))
  263. (goto-char end) (hif-end-of-line) (setq end (point))
  264. (hide-ifdef-region-internal start end)))
  265. (defun hif-show-ifdef-region (start end)
  266. "Everything between START and END is made visible."
  267. (remove-overlays start end 'hide-ifdef t))
  268. ;;===%%SF%% evaluation (Start) ===
  269. ;; It is not useful to set this to anything but `eval'.
  270. ;; In fact, the variable might as well be eliminated.
  271. (defvar hide-ifdef-evaluator 'eval
  272. "The function to use to evaluate a form.
  273. The evaluator is given a canonical form and returns t if text under
  274. that form should be displayed.")
  275. (defvar hif-undefined-symbol nil
  276. "...is by default considered to be false.")
  277. (defun hif-set-var (var value)
  278. "Prepend (var value) pair to hide-ifdef-env."
  279. (setq hide-ifdef-env (cons (cons var value) hide-ifdef-env)))
  280. (defun hif-lookup (var)
  281. ;; (message "hif-lookup %s" var)
  282. (let ((val (assoc var hide-ifdef-env)))
  283. (if val
  284. (cdr val)
  285. hif-undefined-symbol)))
  286. (defun hif-defined (var)
  287. (if (assoc var hide-ifdef-env) 1 0))
  288. ;;===%%SF%% evaluation (End) ===
  289. ;;===%%SF%% parsing (Start) ===
  290. ;;; The code that understands what ifs and ifdef in files look like.
  291. (defconst hif-cpp-prefix "\\(^\\|\r\\)[ \t]*#[ \t]*")
  292. (defconst hif-ifndef-regexp (concat hif-cpp-prefix "ifndef"))
  293. (defconst hif-ifx-regexp (concat hif-cpp-prefix "if\\(n?def\\)?[ \t]+"))
  294. (defconst hif-else-regexp (concat hif-cpp-prefix "else"))
  295. (defconst hif-endif-regexp (concat hif-cpp-prefix "endif"))
  296. (defconst hif-ifx-else-endif-regexp
  297. (concat hif-ifx-regexp "\\|" hif-else-regexp "\\|" hif-endif-regexp))
  298. ;; Used to store the current token and the whole token list during parsing.
  299. ;; Only bound dynamically.
  300. (defvar hif-token)
  301. (defvar hif-token-list)
  302. (defconst hif-token-alist
  303. '(("||" . or)
  304. ("&&" . and)
  305. ("|" . hif-logior)
  306. ("&" . hif-logand)
  307. ("==" . equal)
  308. ("!=" . hif-notequal)
  309. ("!" . not)
  310. ("(" . lparen)
  311. (")" . rparen)
  312. (">" . hif-greater)
  313. ("<" . hif-less)
  314. (">=" . hif-greater-equal)
  315. ("<=" . hif-less-equal)
  316. ("+" . hif-plus)
  317. ("-" . hif-minus)
  318. ("?" . hif-conditional)
  319. (":" . hif-colon)))
  320. (defconst hif-token-regexp
  321. (concat (regexp-opt (mapcar 'car hif-token-alist)) "\\|\\w+"))
  322. (defun hif-tokenize (start end)
  323. "Separate string between START and END into a list of tokens."
  324. (let ((token-list nil))
  325. (with-syntax-table hide-ifdef-syntax-table
  326. (save-excursion
  327. (goto-char start)
  328. (while (progn (forward-comment (point-max)) (< (point) end))
  329. ;; (message "expr-start = %d" expr-start) (sit-for 1)
  330. (cond
  331. ((looking-at "\\\\\n")
  332. (forward-char 2))
  333. ((looking-at hif-token-regexp)
  334. (let ((token (buffer-substring (point) (match-end 0))))
  335. (goto-char (match-end 0))
  336. ;; (message "token: %s" token) (sit-for 1)
  337. (push (or (cdr (assoc token hif-token-alist))
  338. (if (string-equal token "defined") 'hif-defined)
  339. (if (string-match "\\`[0-9]*\\'" token)
  340. (string-to-number token))
  341. (intern token))
  342. token-list)))
  343. (t (error "Bad #if expression: %s" (buffer-string)))))))
  344. (nreverse token-list)))
  345. ;;;-----------------------------------------------------------------
  346. ;;; Translate C preprocessor #if expressions using recursive descent.
  347. ;;; This parser is limited to the operators &&, ||, !, and "defined".
  348. ;;; Added ==, !=, +, and -. Gary Oberbrunner, garyo@avs.com, 8/9/94
  349. (defsubst hif-nexttoken ()
  350. "Pop the next token from token-list into the let variable \"hif-token\"."
  351. (setq hif-token (pop hif-token-list)))
  352. (defun hif-parse-if-exp (token-list)
  353. "Parse the TOKEN-LIST. Return translated list in prefix form."
  354. (let ((hif-token-list token-list))
  355. (hif-nexttoken)
  356. (prog1
  357. (hif-expr)
  358. (if hif-token ; is there still a token?
  359. (error "Error: unexpected token: %s" hif-token)))))
  360. (defun hif-expr ()
  361. "Parse an expression as found in #if.
  362. expr : or-expr | or-expr '?' expr ':' expr."
  363. (let ((result (hif-or-expr))
  364. middle)
  365. (while (eq hif-token 'hif-conditional)
  366. (hif-nexttoken)
  367. (setq middle (hif-expr))
  368. (if (eq hif-token 'hif-colon)
  369. (progn
  370. (hif-nexttoken)
  371. (setq result (list 'hif-conditional result middle (hif-expr))))
  372. (error "Error: unexpected token: %s" hif-token)))
  373. result))
  374. (defun hif-or-expr ()
  375. "Parse n or-expr : and-expr | or-expr '||' and-expr."
  376. (let ((result (hif-and-expr)))
  377. (while (eq hif-token 'or)
  378. (hif-nexttoken)
  379. (setq result (list 'hif-or result (hif-and-expr))))
  380. result))
  381. (defun hif-and-expr ()
  382. "Parse an and-expr : eq-expr | and-expr '&&' eq-expr."
  383. (let ((result (hif-eq-expr)))
  384. (while (eq hif-token 'and)
  385. (hif-nexttoken)
  386. (setq result (list 'hif-and result (hif-eq-expr))))
  387. result))
  388. (defun hif-eq-expr ()
  389. "Parse an eq-expr : math | eq-expr `=='|`!='|`<'|`>'|`>='|`<=' math."
  390. (let ((result (hif-math))
  391. (eq-token nil))
  392. (while (memq hif-token '(equal hif-notequal hif-greater hif-less
  393. hif-greater-equal hif-less-equal))
  394. (setq eq-token hif-token)
  395. (hif-nexttoken)
  396. (setq result (list eq-token result (hif-math))))
  397. result))
  398. (defun hif-math ()
  399. "Parse an expression with + or - and simpler things.
  400. math : factor | math '+|-' factor."
  401. (let ((result (hif-factor))
  402. (math-op nil))
  403. (while (memq hif-token '(hif-plus hif-minus hif-logior hif-logand))
  404. (setq math-op hif-token)
  405. (hif-nexttoken)
  406. (setq result (list math-op result (hif-factor))))
  407. result))
  408. (defun hif-factor ()
  409. "Parse a factor: '!' factor | '(' expr ')' | 'defined(' id ')' | id."
  410. (cond
  411. ((eq hif-token 'not)
  412. (hif-nexttoken)
  413. (list 'hif-not (hif-factor)))
  414. ((eq hif-token 'lparen)
  415. (hif-nexttoken)
  416. (let ((result (hif-expr)))
  417. (if (not (eq hif-token 'rparen))
  418. (error "Bad token in parenthesized expression: %s" hif-token)
  419. (hif-nexttoken)
  420. result)))
  421. ((eq hif-token 'hif-defined)
  422. (hif-nexttoken)
  423. (let ((paren (when (eq hif-token 'lparen) (hif-nexttoken) t))
  424. (ident hif-token))
  425. (if (memq hif-token '(or and not hif-defined lparen rparen))
  426. (error "Error: unexpected token: %s" hif-token))
  427. (when paren
  428. (hif-nexttoken)
  429. (unless (eq hif-token 'rparen)
  430. (error "Error: expected \")\" after identifier")))
  431. (hif-nexttoken)
  432. `(hif-defined (quote ,ident))))
  433. ((numberp hif-token)
  434. (prog1 hif-token (hif-nexttoken)))
  435. ;; Unary plus/minus.
  436. ((memq hif-token '(hif-minus hif-plus))
  437. (list (prog1 hif-token (hif-nexttoken)) 0 (hif-factor)))
  438. (t ; identifier
  439. (let ((ident hif-token))
  440. (if (memq ident '(or and))
  441. (error "Error: missing identifier"))
  442. (hif-nexttoken)
  443. `(hif-lookup (quote ,ident))))))
  444. (defun hif-mathify (val)
  445. "Treat VAL as a number: if it's t or nil, use 1 or 0."
  446. (cond ((eq val t) 1)
  447. ((null val) 0)
  448. (t val)))
  449. (defun hif-conditional (a b c)
  450. (if (not (zerop (hif-mathify a))) (hif-mathify b) (hif-mathify c)))
  451. (defun hif-and (a b)
  452. (and (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
  453. (defun hif-or (a b)
  454. (or (not (zerop (hif-mathify a))) (not (zerop (hif-mathify b)))))
  455. (defun hif-not (a)
  456. (zerop (hif-mathify a)))
  457. (defmacro hif-mathify-binop (fun)
  458. `(lambda (a b)
  459. ,(format "Like `%s' but treat t and nil as 1 and 0." fun)
  460. (,fun (hif-mathify a) (hif-mathify b))))
  461. (defalias 'hif-plus (hif-mathify-binop +))
  462. (defalias 'hif-minus (hif-mathify-binop -))
  463. (defalias 'hif-notequal (hif-mathify-binop /=))
  464. (defalias 'hif-greater (hif-mathify-binop >))
  465. (defalias 'hif-less (hif-mathify-binop <))
  466. (defalias 'hif-greater-equal (hif-mathify-binop >=))
  467. (defalias 'hif-less-equal (hif-mathify-binop <=))
  468. (defalias 'hif-logior (hif-mathify-binop logior))
  469. (defalias 'hif-logand (hif-mathify-binop logand))
  470. ;;;----------- end of parser -----------------------
  471. (defun hif-canonicalize ()
  472. "When at beginning of #ifX, return a Lisp expression for its condition."
  473. (save-excursion
  474. (let ((negate (looking-at hif-ifndef-regexp)))
  475. (re-search-forward hif-ifx-regexp)
  476. (let* ((tokens (hif-tokenize (point)
  477. (progn (hif-end-of-line) (point))))
  478. (expr (hif-parse-if-exp tokens)))
  479. ;; (message "hif-canonicalized: %s" expr)
  480. (if negate
  481. (list 'hif-not expr)
  482. expr)))))
  483. (defun hif-find-any-ifX ()
  484. "Move to next #if..., or #ifndef, at point or after."
  485. ;; (message "find ifX at %d" (point))
  486. (prog1
  487. (re-search-forward hif-ifx-regexp (point-max) t)
  488. (beginning-of-line)))
  489. (defun hif-find-next-relevant ()
  490. "Move to next #if..., #else, or #endif, after the current line."
  491. ;; (message "hif-find-next-relevant at %d" (point))
  492. (end-of-line)
  493. ;; avoid infinite recursion by only going to beginning of line if match found
  494. (if (re-search-forward hif-ifx-else-endif-regexp (point-max) t)
  495. (beginning-of-line)))
  496. (defun hif-find-previous-relevant ()
  497. "Move to previous #if..., #else, or #endif, before the current line."
  498. ;; (message "hif-find-previous-relevant at %d" (point))
  499. (beginning-of-line)
  500. ;; avoid infinite recursion by only going to beginning of line if match found
  501. (if (re-search-backward hif-ifx-else-endif-regexp (point-min) t)
  502. (beginning-of-line)))
  503. (defun hif-looking-at-ifX () ;; Should eventually see #if
  504. (looking-at hif-ifx-regexp))
  505. (defun hif-looking-at-endif ()
  506. (looking-at hif-endif-regexp))
  507. (defun hif-looking-at-else ()
  508. (looking-at hif-else-regexp))
  509. (defun hif-ifdef-to-endif ()
  510. "If positioned at #ifX or #else form, skip to corresponding #endif."
  511. ;; (message "hif-ifdef-to-endif at %d" (point)) (sit-for 1)
  512. (hif-find-next-relevant)
  513. (cond ((hif-looking-at-ifX)
  514. (hif-ifdef-to-endif) ; find endif of nested if
  515. (hif-ifdef-to-endif)) ; find outer endif or else
  516. ((hif-looking-at-else)
  517. (hif-ifdef-to-endif)) ; find endif following else
  518. ((hif-looking-at-endif)
  519. 'done)
  520. (t
  521. (error "Mismatched #ifdef #endif pair"))))
  522. (defun hif-endif-to-ifdef ()
  523. "If positioned at #endif form, skip backward to corresponding #ifX."
  524. ;; (message "hif-endif-to-ifdef at %d" (point))
  525. (let ((start (point)))
  526. (hif-find-previous-relevant)
  527. (if (= start (point))
  528. (error "Mismatched #ifdef #endif pair")))
  529. (cond ((hif-looking-at-endif)
  530. (hif-endif-to-ifdef) ; find beginning of nested if
  531. (hif-endif-to-ifdef)) ; find beginning of outer if or else
  532. ((hif-looking-at-else)
  533. (hif-endif-to-ifdef))
  534. ((hif-looking-at-ifX)
  535. 'done)
  536. (t))) ; never gets here
  537. (defun forward-ifdef (&optional arg)
  538. "Move point to beginning of line of the next ifdef-endif.
  539. With argument, do this that many times."
  540. (interactive "p")
  541. (or arg (setq arg 1))
  542. (if (< arg 0) (backward-ifdef (- arg))
  543. (while (< 0 arg)
  544. (setq arg (- arg))
  545. (let ((start (point)))
  546. (unless (hif-looking-at-ifX)
  547. (hif-find-next-relevant))
  548. (if (hif-looking-at-ifX)
  549. (hif-ifdef-to-endif)
  550. (goto-char start)
  551. (error "No following #ifdef"))))))
  552. (defun backward-ifdef (&optional arg)
  553. "Move point to beginning of the previous ifdef-endif.
  554. With argument, do this that many times."
  555. (interactive "p")
  556. (or arg (setq arg 1))
  557. (if (< arg 0) (forward-ifdef (- arg))
  558. (while (< 0 arg)
  559. (setq arg (1- arg))
  560. (beginning-of-line)
  561. (let ((start (point)))
  562. (unless (hif-looking-at-endif)
  563. (hif-find-previous-relevant))
  564. (if (hif-looking-at-endif)
  565. (hif-endif-to-ifdef)
  566. (goto-char start)
  567. (error "No previous #ifdef"))))))
  568. (defun down-ifdef ()
  569. "Move point to beginning of nested ifdef or else-part."
  570. (interactive)
  571. (let ((start (point)))
  572. (hif-find-next-relevant)
  573. (if (or (hif-looking-at-ifX) (hif-looking-at-else))
  574. ()
  575. (goto-char start)
  576. (error "No following #ifdef"))))
  577. (defun up-ifdef ()
  578. "Move point to beginning of enclosing ifdef or else-part."
  579. (interactive)
  580. (beginning-of-line)
  581. (let ((start (point)))
  582. (unless (hif-looking-at-endif)
  583. (hif-find-previous-relevant))
  584. (if (hif-looking-at-endif)
  585. (hif-endif-to-ifdef))
  586. (if (= start (point))
  587. (error "No previous #ifdef"))))
  588. (defun next-ifdef (&optional arg)
  589. "Move to the beginning of the next #ifX, #else, or #endif.
  590. With argument, do this that many times."
  591. (interactive "p")
  592. (or arg (setq arg 1))
  593. (if (< arg 0) (previous-ifdef (- arg))
  594. (while (< 0 arg)
  595. (setq arg (1- arg))
  596. (hif-find-next-relevant)
  597. (when (eolp)
  598. (beginning-of-line)
  599. (error "No following #ifdefs, #elses, or #endifs")))))
  600. (defun previous-ifdef (&optional arg)
  601. "Move to the beginning of the previous #ifX, #else, or #endif.
  602. With argument, do this that many times."
  603. (interactive "p")
  604. (or arg (setq arg 1))
  605. (if (< arg 0) (next-ifdef (- arg))
  606. (while (< 0 arg)
  607. (setq arg (1- arg))
  608. (let ((start (point)))
  609. (hif-find-previous-relevant)
  610. (if (= start (point))
  611. (error "No previous #ifdefs, #elses, or #endifs"))))))
  612. ;;===%%SF%% parsing (End) ===
  613. ;;===%%SF%% hide-ifdef-hiding (Start) ===
  614. ;;; A range is a structure with four components:
  615. ;;; ELSE-P True if there was an else clause for the ifdef.
  616. ;;; START The start of the range. (beginning of line)
  617. ;;; ELSE The else marker (beginning of line)
  618. ;;; Only valid if ELSE-P is true.
  619. ;;; END The end of the range. (beginning of line)
  620. (defsubst hif-make-range (start end &optional else)
  621. (list start else end))
  622. (defsubst hif-range-start (range) (elt range 0))
  623. (defsubst hif-range-else (range) (elt range 1))
  624. (defsubst hif-range-end (range) (elt range 2))
  625. ;;; Find-Range
  626. ;;; The workhorse, it delimits the #if region. Reasonably simple:
  627. ;;; Skip until an #else or #endif is found, remembering positions. If
  628. ;;; an #else was found, skip some more, looking for the true #endif.
  629. (defun hif-find-range ()
  630. "Return a Range structure describing the current #if region.
  631. Point is left unchanged."
  632. ;; (message "hif-find-range at %d" (point))
  633. (save-excursion
  634. (beginning-of-line)
  635. (let ((start (point))
  636. (else nil)
  637. (end nil))
  638. ;; Part one. Look for either #endif or #else.
  639. ;; This loop-and-a-half dedicated to E. Dijkstra.
  640. (while (progn
  641. (hif-find-next-relevant)
  642. (hif-looking-at-ifX)) ; Skip nested ifdef
  643. (hif-ifdef-to-endif))
  644. ;; Found either a #else or an #endif.
  645. (cond ((hif-looking-at-else)
  646. (setq else (point)))
  647. (t
  648. (setq end (point)))) ; (line-end-position)
  649. ;; If found #else, look for #endif.
  650. (when else
  651. (while (progn
  652. (hif-find-next-relevant)
  653. (hif-looking-at-ifX)) ; Skip nested ifdef
  654. (hif-ifdef-to-endif))
  655. (if (hif-looking-at-else)
  656. (error "Found two elses in a row? Broken!"))
  657. (setq end (point))) ; (line-end-position)
  658. (hif-make-range start end else))))
  659. ;;; A bit slimy.
  660. (defun hif-hide-line (point)
  661. "Hide the line containing point. Does nothing if `hide-ifdef-lines' is nil."
  662. (when hide-ifdef-lines
  663. (save-excursion
  664. (goto-char point)
  665. (hide-ifdef-region-internal
  666. (line-beginning-position) (progn (hif-end-of-line) (point))))))
  667. ;;; Hif-Possibly-Hide
  668. ;;; There are four cases. The #ifX expression is "taken" if it
  669. ;;; the hide-ifdef-evaluator returns T. Presumably, this means the code
  670. ;;; inside the #ifdef would be included when the program was
  671. ;;; compiled.
  672. ;;;
  673. ;;; Case 1: #ifX taken, and there's an #else.
  674. ;;; The #else part must be hidden. The #if (then) part must be
  675. ;;; processed for nested #ifX's.
  676. ;;; Case 2: #ifX taken, and there's no #else.
  677. ;;; The #if part must be processed for nested #ifX's.
  678. ;;; Case 3: #ifX not taken, and there's an #else.
  679. ;;; The #if part must be hidden. The #else part must be processed
  680. ;;; for nested #ifs.
  681. ;;; Case 4: #ifX not taken, and there's no #else.
  682. ;;; The #ifX part must be hidden.
  683. ;;;
  684. ;;; Further processing is done by narrowing to the relevant region
  685. ;;; and just recursively calling hide-ifdef-guts.
  686. ;;;
  687. ;;; When hif-possibly-hide returns, point is at the end of the
  688. ;;; possibly-hidden range.
  689. (defun hif-recurse-on (start end)
  690. "Call `hide-ifdef-guts' after narrowing to end of START line and END line."
  691. (save-excursion
  692. (save-restriction
  693. (goto-char start)
  694. (end-of-line)
  695. (narrow-to-region (point) end)
  696. (hide-ifdef-guts))))
  697. (defun hif-possibly-hide ()
  698. "Called at #ifX expression, this hides those parts that should be hidden.
  699. It uses the judgment of `hide-ifdef-evaluator'."
  700. ;; (message "hif-possibly-hide") (sit-for 1)
  701. (let ((test (hif-canonicalize))
  702. (range (hif-find-range)))
  703. ;; (message "test = %s" test) (sit-for 1)
  704. (hif-hide-line (hif-range-end range))
  705. (if (not (hif-not (funcall hide-ifdef-evaluator test)))
  706. (cond ((hif-range-else range) ; case 1
  707. (hif-hide-line (hif-range-else range))
  708. (hide-ifdef-region (hif-range-else range)
  709. (1- (hif-range-end range)))
  710. (hif-recurse-on (hif-range-start range)
  711. (hif-range-else range)))
  712. (t ; case 2
  713. (hif-recurse-on (hif-range-start range)
  714. (hif-range-end range))))
  715. (cond ((hif-range-else range) ; case 3
  716. (hif-hide-line (hif-range-else range))
  717. (hide-ifdef-region (hif-range-start range)
  718. (1- (hif-range-else range)))
  719. (hif-recurse-on (hif-range-else range)
  720. (hif-range-end range)))
  721. (t ; case 4
  722. (hide-ifdef-region (point)
  723. (1- (hif-range-end range))))))
  724. (hif-hide-line (hif-range-start range)) ; Always hide start.
  725. (goto-char (hif-range-end range))
  726. (end-of-line)))
  727. (defun hide-ifdef-guts ()
  728. "Does most of the work of `hide-ifdefs'.
  729. It does not do the work that's pointless to redo on a recursive entry."
  730. ;; (message "hide-ifdef-guts")
  731. (save-excursion
  732. (goto-char (point-min))
  733. (while (hif-find-any-ifX)
  734. (hif-possibly-hide))))
  735. ;;===%%SF%% hide-ifdef-hiding (End) ===
  736. ;;===%%SF%% exports (Start) ===
  737. (defun hide-ifdef-toggle-read-only ()
  738. "Toggle `hide-ifdef-read-only'."
  739. (interactive)
  740. (setq hide-ifdef-read-only (not hide-ifdef-read-only))
  741. (message "Hide-Read-Only %s"
  742. (if hide-ifdef-read-only "ON" "OFF"))
  743. (if hide-ifdef-hiding
  744. (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
  745. (force-mode-line-update))
  746. (defun hide-ifdef-toggle-outside-read-only ()
  747. "Replacement for `toggle-read-only' within Hide-Ifdef mode."
  748. (interactive)
  749. (setq hif-outside-read-only (not hif-outside-read-only))
  750. (message "Read only %s"
  751. (if hif-outside-read-only "ON" "OFF"))
  752. (setq buffer-read-only
  753. (or (and hide-ifdef-hiding hide-ifdef-read-only)
  754. hif-outside-read-only))
  755. (force-mode-line-update))
  756. (defun hide-ifdef-toggle-shadowing ()
  757. "Toggle shadowing."
  758. (interactive)
  759. (set (make-local-variable 'hide-ifdef-shadow) (not hide-ifdef-shadow))
  760. (message "Shadowing %s" (if hide-ifdef-shadow "ON" "OFF"))
  761. (save-restriction
  762. (widen)
  763. (dolist (overlay (overlays-in (point-min) (point-max)))
  764. (when (overlay-get overlay 'hide-ifdef)
  765. (if hide-ifdef-shadow
  766. (progn
  767. (overlay-put overlay 'invisible nil)
  768. (overlay-put overlay 'face 'hide-ifdef-shadow))
  769. (overlay-put overlay 'face nil)
  770. (overlay-put overlay 'invisible 'hide-ifdef))))))
  771. (defun hide-ifdef-define (var)
  772. "Define a VAR so that #ifdef VAR would be included."
  773. (interactive "SDefine what? ")
  774. (hif-set-var var 1)
  775. (if hide-ifdef-hiding (hide-ifdefs)))
  776. (defun hide-ifdef-undef (var)
  777. "Undefine a VAR so that #ifdef VAR would not be included."
  778. (interactive "SUndefine what? ")
  779. (hif-set-var var nil)
  780. (if hide-ifdef-hiding (hide-ifdefs)))
  781. (defun hide-ifdefs (&optional nomsg)
  782. "Hide the contents of some #ifdefs.
  783. Assume that defined symbols have been added to `hide-ifdef-env'.
  784. The text hidden is the text that would not be included by the C
  785. preprocessor if it were given the file with those symbols defined.
  786. Turn off hiding by calling `show-ifdefs'."
  787. (interactive)
  788. (message "Hiding...")
  789. (setq hif-outside-read-only buffer-read-only)
  790. (unless hide-ifdef-mode (hide-ifdef-mode 1)) ; turn on hide-ifdef-mode
  791. (if hide-ifdef-hiding
  792. (show-ifdefs)) ; Otherwise, deep confusion.
  793. (setq hide-ifdef-hiding t)
  794. (hide-ifdef-guts)
  795. (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only))
  796. (or nomsg
  797. (message "Hiding done")))
  798. (defun show-ifdefs ()
  799. "Cancel the effects of `hide-ifdef': show the contents of all #ifdefs."
  800. (interactive)
  801. (setq buffer-read-only hif-outside-read-only)
  802. (hif-show-all)
  803. (setq hide-ifdef-hiding nil))
  804. (defun hif-find-ifdef-block ()
  805. "Utility for hide and show `ifdef-block'.
  806. Return as (TOP . BOTTOM) the extent of ifdef block."
  807. (let (max-bottom)
  808. (cons (save-excursion
  809. (beginning-of-line)
  810. (unless (or (hif-looking-at-else) (hif-looking-at-ifX))
  811. (up-ifdef))
  812. (prog1 (point)
  813. (hif-ifdef-to-endif)
  814. (setq max-bottom (1- (point)))))
  815. (save-excursion
  816. (beginning-of-line)
  817. (unless (hif-looking-at-endif)
  818. (hif-find-next-relevant))
  819. (while (hif-looking-at-ifX)
  820. (hif-ifdef-to-endif)
  821. (hif-find-next-relevant))
  822. (min max-bottom (1- (point)))))))
  823. (defun hide-ifdef-block ()
  824. "Hide the ifdef block (true or false part) enclosing or before the cursor."
  825. (interactive)
  826. (unless hide-ifdef-mode (hide-ifdef-mode 1))
  827. (let ((top-bottom (hif-find-ifdef-block)))
  828. (hide-ifdef-region (car top-bottom) (cdr top-bottom))
  829. (when hide-ifdef-lines
  830. (hif-hide-line (car top-bottom))
  831. (hif-hide-line (1+ (cdr top-bottom))))
  832. (setq hide-ifdef-hiding t))
  833. (setq buffer-read-only (or hide-ifdef-read-only hif-outside-read-only)))
  834. (defun show-ifdef-block ()
  835. "Show the ifdef block (true or false part) enclosing or before the cursor."
  836. (interactive)
  837. (let ((top-bottom (hif-find-ifdef-block)))
  838. (if hide-ifdef-lines
  839. (hif-show-ifdef-region
  840. (save-excursion
  841. (goto-char (car top-bottom)) (line-beginning-position))
  842. (save-excursion
  843. (goto-char (1+ (cdr top-bottom)))
  844. (hif-end-of-line) (point)))
  845. (hif-show-ifdef-region (1- (car top-bottom)) (cdr top-bottom)))))
  846. ;;; definition alist support
  847. (defvar hide-ifdef-define-alist nil
  848. "A global assoc list of pre-defined symbol lists.")
  849. (defun hif-compress-define-list (env)
  850. "Compress the define list ENV into a list of defined symbols only."
  851. (let ((new-defs nil))
  852. (dolist (def env new-defs)
  853. (if (hif-lookup (car def)) (push (car def) new-defs)))))
  854. (defun hide-ifdef-set-define-alist (name)
  855. "Set the association for NAME to `hide-ifdef-env'."
  856. (interactive "SSet define list: ")
  857. (push (cons name (hif-compress-define-list hide-ifdef-env))
  858. hide-ifdef-define-alist))
  859. (defun hide-ifdef-use-define-alist (name)
  860. "Set `hide-ifdef-env' to the define list specified by NAME."
  861. (interactive
  862. (list (completing-read "Use define list: "
  863. (mapcar (lambda (x) (symbol-name (car x)))
  864. hide-ifdef-define-alist)
  865. nil t)))
  866. (if (stringp name) (setq name (intern name)))
  867. (let ((define-list (assoc name hide-ifdef-define-alist)))
  868. (if define-list
  869. (setq hide-ifdef-env
  870. (mapcar (lambda (arg) (cons arg t))
  871. (cdr define-list)))
  872. (error "No define list for %s" name))
  873. (if hide-ifdef-hiding (hide-ifdefs))))
  874. (provide 'hideif)
  875. ;;; hideif.el ends here