]> code.delx.au - gnu-emacs/blob - lisp/progmodes/hideshow.el
*** empty log message ***
[gnu-emacs] / lisp / progmodes / hideshow.el
1 ;;; hideshow.el --- minor mode cmds to selectively display blocks of code
2
3 ;; Copyright (C) 1994, 95, 96, 97, 98 Free Software Foundation
4
5 ;; Author: Thien-Thi Nguyen <ttn@netcom.com>
6 ;; Dan Nicolaescu <done@ece.arizona.edu>
7 ;; Keywords: C C++ java lisp tools editing comments blocks hiding outlines
8 ;; Maintainer-Version: 4.22
9 ;; Time-of-Day-Author-Most-Likely-to-be-Recalcitrant: early morning
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29
30 ;; - Commands provided
31 ;;
32 ;; This file provides `hs-minor-mode'. When active, seven commands:
33 ;;
34 ;; hs-{hide,show}-{all,block}, hs-show-region,
35 ;; hs-hide-level and hs-minor-mode
36 ;;
37 ;; are available, implementing block hiding and showing. Blocks are
38 ;; defined per mode. In c-mode or c++-mode, they are simply curly braces,
39 ;; while in Lisp-ish modes they are parens. Multi-line comments can also
40 ;; be hidden. The command `M-x hs-minor-mode' toggles the minor mode or
41 ;; sets it (similar to outline minor mode).
42
43 ;; - Customization
44 ;;
45 ;; Variables control things thusly:
46 ;;
47 ;; hs-hide-comments-when-hiding-all -- self-explanatory!
48 ;; hs-show-hidden-short-form -- whether or not the last line in a form
49 ;; is omitted (saving screen space)
50 ;; hs-isearch-open -- what kind of hidden blocks to open when
51 ;; doing isearch
52 ;; hs-special-modes-alist -- keeps at bay hideshow's heuristics with
53 ;; respect to block definitions
54 ;;
55 ;; Hooks are run after some commands:
56 ;;
57 ;; hs-hide-hook in hs-hide-block, hs-hide-all, hs-hide-level
58 ;; hs-show-hook hs-show-block, hs-show-all, hs-show-region
59 ;;
60 ;; See docs for each variable or hook for more info.
61
62 ;; - Suggested usage
63 ;;
64 ;; (load-library "hideshow")
65 ;; (add-hook 'X-mode-hook 'hs-minor-mode) ; other modes similarly
66 ;;
67 ;; where X = {emacs-lisp,c,c++,perl,...}. See the doc for the variable
68 ;; `hs-special-modes-alist' if you'd like to use hideshow w/ other modes.
69
70 ;; - Bugs / caveats
71 ;;
72 ;; 1. Hideshow does not work w/ emacs 18 because emacs 18 lacks the
73 ;; function `forward-comment' (among other things). If someone writes
74 ;; this, please send me a copy.
75 ;;
76 ;; 2. Users of cc-mode.el should not hook hideshow into
77 ;; c-mode-common-hook since at that stage of the call sequence, the
78 ;; variables `comment-start' and `comment-end' are not yet provided.
79 ;; Instead, use c-mode-hook and c++-mode-hook as suggested above.
80
81 ;; - Thanks and feedback
82 ;;
83 ;; Thanks go to the following people for valuable ideas, code and bug
84 ;; reports.
85 ;; adahome@ix.netcom.com Dean Andrews
86 ;; alfh@ifi.uio.no Alf-Ivar Holm
87 ;; gael@gnlab030.grenoble.hp.com Gael Marziou
88 ;; jan.djarv@sa.erisoft.se Jan Djarv
89 ;; preston.f.crow@dartmouth.edu Preston F. Crow
90 ;; qhslali@aom.ericsson.se Lars Lindberg
91 ;; sheff@edcsgw2.cr.usgs.gov Keith Sheffield
92 ;; ware@cis.ohio-state.edu Pete Ware
93 ;; d.love@dl.ac.uk Dave Love
94 ;;
95 ;; Special thanks go to Dan Nicolaescu <done@ece.arizona.edu>, who
96 ;; reimplemented hideshow using overlays (rather than selective display),
97 ;; added isearch magic, folded in custom.el compatibility, generalized
98 ;; comment handling, incorporated mouse support, and maintained the code
99 ;; in general. Version 4.0 is largely due to his efforts.
100 ;;
101 ;; Correspondance welcome; please indicate version number.
102
103 ;;; Code:
104
105 (require 'easymenu)
106
107 ;;;----------------------------------------------------------------------------
108 ;;; user-configurable variables
109
110 (defgroup hideshow nil
111 "Minor mode for hiding and showing program and comment blocks."
112 :prefix "hs-"
113 :group 'languages)
114
115 ;;;###autoload
116 (defcustom hs-hide-comments-when-hiding-all t
117 "Hide the comments too when you do an `hs-hide-all'."
118 :type 'boolean
119 :group 'hideshow)
120
121 ;;;###autoload
122 (defcustom hs-show-hidden-short-form t
123 "Leave only the first line visible in a hidden block.
124 If non-nil only the first line is visible when a block is in the
125 hidden state, else both the first line and the last line are shown.
126 A nil value disables `hs-adjust-block-beginning', which see.
127
128 An example of how this works: (in C mode)
129 original:
130
131 /* My function main
132 some more stuff about main
133 */
134 int
135 main(void)
136 {
137 int x=0;
138 return 0;
139 }
140
141
142 hidden and `hs-show-hidden-short-form' is nil
143 /* My function main...
144 */
145 int
146 main(void)
147 {...
148 }
149
150 hidden and `hs-show-hidden-short-form' is t
151 /* My function main...
152 int
153 main(void)...
154
155 For the last case you have to be on the line containing the
156 ellipsis when you do `hs-show-block'."
157 :type 'boolean
158 :group 'hideshow)
159
160 (defcustom hs-minor-mode-hook 'hs-hide-initial-comment-block
161 "Hook called when `hs-minor-mode' is installed.
162 A good value for this would be `hs-hide-initial-comment-block' to
163 hide all the comments at the beginning of the file."
164 :type 'hook
165 :group 'hideshow)
166
167 (defcustom hs-isearch-open 'block
168 "What kind of hidden blocks to open when doing `isearch'.
169 One of the following values:
170
171 block -- open only blocks
172 comment -- open only comments
173 t -- open both blocks and comments
174 nil -- open neither blocks nor comments
175
176 This has effect iff `search-invisible' is set to `open'."
177 :type '(choice (const :tag "open only blocks" block)
178 (const :tag "open only comments" comment)
179 (const :tag "open both blocks and comments" t)
180 (const :tag "don't open any of them" nil))
181 :group 'hideshow)
182
183 ;;;###autoload
184 (defvar hs-special-modes-alist
185 '((c-mode "{" "}" nil nil hs-c-like-adjust-block-beginning)
186 (c++-mode "{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning)
187 (java-mode "\\(\\(\\([ \t]*\\(\\(abstract\\|final\\|native\\|p\\(r\\(ivate\\|otected\\)\\|ublic\\)\\|s\\(tatic\\|ynchronized\\)\\)[ \t\n]+\\)*[.a-zA-Z0-9_:]+[ \t\n]*\\(\\[[ \t\n]*\\][ \t\n]*\\)?\\([a-zA-Z0-9_:]+[ \t\n]*\\)([^)]*)\\([ \n\t]+throws[ \t\n][^{]+\\)?\\)\\|\\([ \t]*static[^{]*\\)\\)[ \t\n]*{\\)" "}" "/[*/]" java-hs-forward-sexp hs-c-like-adjust-block-beginning))
188 ; I tested the java regexp using the following:
189 ;(defvar hsj-public)
190 ;(defvar hsj-type)
191 ;(defvar hsj-fname)
192 ;(defvar hsj-par)
193 ;(defvar hsj-throws)
194 ;(defvar hsj-static)
195
196 ;(setq hsj-public
197 ; (concat "[ \t]*\\("
198 ; (regexp-opt '("public" "private" "protected" "abstract"
199 ; "synchronized" "static" "final" "native") 1)
200 ; "[ \t\n]+\\)*"))
201
202 ;(setq hsj-type "[.a-zA-Z0-9_:]+[ \t\n]*\\(\\[[ \t\n]*\\][ \t\n]*\\)?")
203 ;(setq hsj-fname "\\([a-zA-Z0-9_:]+[ \t\n]*\\)")
204 ;(setq hsj-par "([^)]*)")
205 ;(setq hsj-throws "\\([ \n\t]+throws[ \t\n][^{]+\\)?")
206
207 ;(setq hsj-static "[ \t]*static[^{]*")
208
209
210 ;(setq hs-block-start-regexp (concat
211 ; "\\("
212 ; "\\("
213 ; "\\("
214 ; hsj-public
215 ; hsj-type
216 ; hsj-fname
217 ; hsj-par
218 ; hsj-throws
219 ; "\\)"
220 ; "\\|"
221 ; "\\("
222 ; hsj-static
223 ; "\\)"
224 ; "\\)"
225 ; "[ \t\n]*{"
226 ; "\\)"
227 ; ))
228
229 "*Alist for initializing the hideshow variables for different modes.
230 It has the form
231 (MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC).
232 If present, hideshow will use these values as regexps for start, end
233 and comment-start, respectively. Since Algol-ish languages do not have
234 single-character block delimiters, the function `forward-sexp' used
235 by hideshow doesn't work. In this case, if a similar function is
236 available, you can register it and have hideshow use it instead of
237 `forward-sexp'. See the documentation for `hs-adjust-block-beginning'
238 to see what is the use of ADJUST-BEG-FUNC.
239
240 If any of those is left nil, hideshow will try to guess some values
241 using function `hs-grok-mode-type'.
242
243 Note that the regexps should not contain leading or trailing whitespace.")
244
245 (defvar hs-hide-hook nil
246 "*Hooks called at the end of commands to hide text.
247 These commands include `hs-hide-all', `hs-hide-block' and `hs-hide-level'.")
248
249 (defvar hs-show-hook nil
250 "*Hooks called at the end of commands to show text.
251 These commands include `hs-show-all', `hs-show-block' and `hs-show-region'.")
252
253 (defvar hs-minor-mode-prefix "\C-c"
254 "*Prefix key to use for hideshow commands in hideshow minor mode.")
255
256 ;;;----------------------------------------------------------------------------
257 ;;; internal variables
258
259 (defvar hs-minor-mode nil
260 "Non-nil if using hideshow mode as a minor mode of some other mode.
261 Use the command `hs-minor-mode' to toggle this variable.")
262
263 (defvar hs-minor-mode-map nil
264 "Mode map for hideshow minor mode.")
265
266 ;(defvar hs-menu-bar nil
267 ; "Menu bar for hideshow minor mode (Xemacs only).")
268
269 (defvar hs-c-start-regexp nil
270 "Regexp for beginning of comments.
271 Differs from mode-specific comment regexps in that
272 surrounding whitespace is stripped.")
273
274 (defvar hs-block-start-regexp nil
275 "Regexp for beginning of block.")
276
277 (defvar hs-block-end-regexp nil
278 "Regexp for end of block.")
279
280 (defvar hs-forward-sexp-func 'forward-sexp
281 "Function used to do a `forward-sexp'.
282 Should change for Algol-ish modes. For single-character block
283 delimiters -- ie, the syntax table regexp for the character is
284 either `(' or `)' -- `hs-forward-sexp-func' would just be
285 `forward-sexp'. For other modes such as simula, a more specialized
286 function is necessary.")
287
288 (defvar hs-adjust-block-beginning nil
289 "Function used to tweak the block beginning.
290 It has effect only if `hs-show-hidden-short-form' is non-nil.
291 The block it is hidden from the point returned by this function,
292 as opposed to hiding it from the point returned when searching
293 `hs-block-start-regexp'. In c-like modes, if we wish to also hide the
294 curly braces (if you think they occupy too much space on the screen),
295 this function should return the starting point (at the end of line) of
296 the hidden region.
297
298 It is called with a single argument ARG which is the the position in
299 buffer after the block beginning.
300
301 It should return the position from where we should start hiding.
302
303 It should not move the point.
304
305 See `hs-c-like-adjust-block-beginning' for an example of using this.")
306
307 ;(defvar hs-emacs-type 'fsf
308 ; "Used to support both Emacs and Xemacs.")
309
310 ;(eval-when-compile
311 ; (if (string-match "xemacs\\|lucid" emacs-version)
312 ; (progn
313 ; (defvar current-menubar nil "")
314 ; (defun set-buffer-menubar (arg1))
315 ; (defun add-menu (arg1 arg2 arg3)))))
316
317 ;;;----------------------------------------------------------------------------
318 ;;; support funcs
319
320 ;; snarfed from outline.el;
321 (defun hs-flag-region (from to flag)
322 "Hide or show lines from FROM to TO, according to FLAG.
323 If FLAG is nil then text is shown, while if FLAG is non-nil the text
324 is hidden. Actually flag is really either `comment' or `block'
325 depending on what kind of block it is suppose to hide."
326 (save-excursion
327 (goto-char from)
328 (end-of-line)
329 (hs-discard-overlays (point) to 'invisible 'hs)
330 (if flag
331 (let ((overlay (make-overlay (point) to)))
332 ;; Make overlay hidden and intangible.
333 (overlay-put overlay 'invisible 'hs)
334 (overlay-put overlay 'hs t)
335 (when (or (eq hs-isearch-open t) (eq hs-isearch-open flag))
336 (overlay-put overlay 'isearch-open-invisible
337 'hs-isearch-open-invisible))
338 (overlay-put overlay 'intangible t)))))
339
340 ;; This is set as an `isearch-open-invisible' property to hidden
341 ;; overlays.
342 (defun hs-isearch-open-invisible (ov)
343 (save-excursion
344 (goto-char (overlay-start ov))
345 (hs-show-block)))
346
347 ;; Remove from the region BEG ... END all overlays
348 ;; with a PROP property equal to VALUE.
349 ;; Overlays with a PROP property different from VALUE are not touched.
350 (defun hs-discard-overlays (beg end prop value)
351 (if (< end beg)
352 (setq beg (prog1 end (setq end beg))))
353 (save-excursion
354 (goto-char beg)
355 (let ((overlays (overlays-in beg end))
356 o)
357 (while overlays
358 (setq o (car overlays))
359 (if (eq (overlay-get o prop) value)
360 (delete-overlay o))
361 (setq overlays (cdr overlays))))))
362
363 (defun hs-hide-block-at-point (&optional end comment-reg)
364 "Hide block iff on block beginning.
365 Optional arg END means reposition at end.
366 Optional arg COMMENT-REG is a list of the form (BEGIN . END) and
367 specifies the limits of the comment, or nil if the block is not
368 a comment."
369 (if comment-reg
370 (progn
371 ;; goto the end of line at the end of the comment
372 (goto-char (nth 1 comment-reg))
373 (unless hs-show-hidden-short-form (forward-line -1))
374 (end-of-line)
375 (hs-flag-region (car comment-reg) (point) 'comment)
376 (goto-char (if end (nth 1 comment-reg) (car comment-reg))))
377 (if (looking-at hs-block-start-regexp)
378 (let* ((p ;; p is the point at the end of the block beginning
379 (if (and hs-show-hidden-short-form
380 hs-adjust-block-beginning)
381 ;; we need to adjust the block beginning
382 (funcall hs-adjust-block-beginning (match-end 0))
383 (match-end 0)))
384 ;; q is the point at the end of the block
385 (q (progn (funcall hs-forward-sexp-func 1) (point))))
386 ;; position the point so we can call `hs-flag-region'
387 (unless hs-show-hidden-short-form (forward-line -1))
388 (end-of-line)
389 (if (and (< p (point)) (> (count-lines p q)
390 (if hs-show-hidden-short-form 1 2)))
391 (hs-flag-region p (point) 'block))
392 (goto-char (if end q p))))))
393
394 (defun hs-show-block-at-point (&optional end comment-reg)
395 "Show block iff on block beginning.
396 Optional arg END means reposition at end.
397 Optional arg COMMENT-REG is a list of the forme (BEGIN . END) and
398 specifies the limits of the comment. It should be nil when hiding
399 a block."
400 (if comment-reg
401 (when (car comment-reg)
402 (hs-flag-region (car comment-reg) (nth 1 comment-reg) nil)
403 (goto-char (if end (nth 1 comment-reg) (car comment-reg))))
404 (if (looking-at hs-block-start-regexp)
405 (let* ((p (point))
406 (q
407 (condition-case error ; probably unbalanced paren
408 (progn
409 (funcall hs-forward-sexp-func 1)
410 (point))
411 (error
412 ;; try to get out of rat's nest and expose the whole func
413 (if (/= (current-column) 0) (beginning-of-defun))
414 (setq p (point))
415 (re-search-forward (concat "^" hs-block-start-regexp)
416 (point-max) t 2)
417 (point)))))
418 (hs-flag-region p q nil)
419 (goto-char (if end (1+ (point)) p))))))
420
421 (defun hs-safety-is-job-n ()
422 "Warn if `buffer-invisibility-spec' does not contain hs."
423 (if (or buffer-invisibility-spec (assq 'hs buffer-invisibility-spec) )
424 nil
425 (message "Warning: `buffer-invisibility-spec' does not contain hs!!")
426 (sit-for 2)))
427
428 (defun hs-hide-initial-comment-block ()
429 (interactive)
430 "Hide the first block of comments in a file.
431 This is useful when a part of `hs-minor-mode-hook', especially with
432 huge header-comment RCS logs."
433 (let ((p (point))
434 c-reg)
435 (goto-char (point-min))
436 (skip-chars-forward " \t\n^L")
437 (setq c-reg (hs-inside-comment-p))
438 ;; see if we have enough comment lines to hide
439 (if (and c-reg (> (count-lines (car c-reg) (nth 1 c-reg))
440 (if hs-show-hidden-short-form 1 2)))
441 (hs-hide-block)
442 (goto-char p))))
443
444 (defun hs-inside-comment-p ()
445 "Return non-nil if point is inside a comment, otherwise nil.
446 Actually, returns a list containing the buffer position of the start
447 and the end of the comment. A comment block can be hidden only if on
448 its starting line there is only whitespace preceding the actual comment
449 beginning. If we are inside of a comment but this condition is not met,
450 we return a list having a nil as its car and the end of comment position
451 as cdr."
452 (save-excursion
453 ;; the idea is to look backwards for a comment start regexp, do a
454 ;; forward comment, and see if we are inside, then extend extend
455 ;; forward and backward as long as we have comments
456 (let ((q (point)))
457 (when (or (looking-at hs-c-start-regexp)
458 (re-search-backward hs-c-start-regexp (point-min) t))
459 (forward-comment (- (buffer-size)))
460 (skip-chars-forward " \t\n\f")
461 (let ((p (point))
462 (not-hidable nil))
463 (beginning-of-line)
464 (unless (looking-at (concat "[ \t]*" hs-c-start-regexp))
465 ;; we are in this situation: (example)
466 ;; (defun bar ()
467 ;; (foo)
468 ;; ) ; comment
469 ;; ^
470 ;; the point was here before doing (beginning-of-line)
471 ;; here we should advance till the next comment which
472 ;; eventually has only white spaces preceding it on the same
473 ;; line
474 (goto-char p)
475 (forward-comment 1)
476 (skip-chars-forward " \t\n\f")
477 (setq p (point))
478 (while (and (< (point) q)
479 (> (point) p)
480 (not (looking-at hs-c-start-regexp)))
481 (setq p (point)) ;; use this to avoid an infinit cycle.
482 (forward-comment 1)
483 (skip-chars-forward " \t\n\f"))
484 (if (or (not (looking-at hs-c-start-regexp))
485 (> (point) q))
486 ;; we cannot hide this comment block
487 (setq not-hidable t)))
488 ;; goto the end of the comment
489 (forward-comment (buffer-size))
490 (skip-chars-backward " \t\n\f")
491 (end-of-line)
492 (if (>= (point) q)
493 (list (if not-hidable nil p) (point))))))))
494
495 (defun hs-grok-mode-type ()
496 "Set up hideshow variables for new buffers.
497 If `hs-special-modes-alist' has information associated with the
498 current buffer's major mode, use that.
499 Otherwise, guess start, end and comment-start regexps; forward-sexp
500 function; and adjust-block-beginning function."
501 (if (and (boundp 'comment-start)
502 (boundp 'comment-end)
503 comment-start comment-end)
504 (let ((lookup (assoc major-mode hs-special-modes-alist)))
505 (setq hs-block-start-regexp (or (nth 1 lookup) "\\s\(")
506 hs-block-end-regexp (or (nth 2 lookup) "\\s\)")
507 hs-c-start-regexp (or (nth 3 lookup)
508 (let ((c-start-regexp
509 (regexp-quote comment-start)))
510 (if (string-match " +$" c-start-regexp)
511 (substring c-start-regexp 0 (1- (match-end 0)))
512 c-start-regexp)))
513 hs-forward-sexp-func (or (nth 4 lookup) 'forward-sexp)
514 hs-adjust-block-beginning (nth 5 lookup)))
515 (error "%s Mode doesn't support Hideshow Mode" mode-name)))
516
517 (defun hs-find-block-beginning ()
518 "Reposition point at block-start.
519 Return point, or nil if top-level."
520 (let (done
521 (try-again t)
522 (here (point))
523 (both-regexps (concat "\\(" hs-block-start-regexp "\\)\\|\\("
524 hs-block-end-regexp "\\)"))
525 (buf-size (buffer-size)))
526 (beginning-of-line)
527 ;; A block beginning can span on multiple lines, if the point
528 ;; is on one of those lines, trying a regexp search from
529 ;; that point would fail to find the block beginning, so we look
530 ;; backwards for the block beginning, or a block end.
531 (while try-again
532 (setq try-again nil)
533 (if (and (re-search-backward both-regexps (point-min) t)
534 (match-beginning 1)) ; found a block beginning
535 (if (save-match-data (hs-inside-comment-p))
536 ;;but it was inside a comment, so we have to look for
537 ;;it again
538 (setq try-again t)
539 ;; that's what we were looking for
540 (setq done (match-beginning 0)))
541 ;; we found a block end, or we reached the beginning of the
542 ;; buffer look to see if we were on a block beginning when we
543 ;; started
544 (if (and
545 (re-search-forward hs-block-start-regexp (point-max) t)
546 (or
547 (and (>= here (match-beginning 0)) (< here (match-end 0)))
548 (and hs-show-hidden-short-form hs-adjust-block-beginning
549 (save-match-data
550 (= 1 (count-lines
551 (funcall hs-adjust-block-beginning
552 (match-end 0)) here))))))
553 (setq done (match-beginning 0)))))
554 (goto-char here)
555 (while (and (not done)
556 ;; This had problems because the regexp can match something
557 ;; inside of a comment!
558 ;; Since inside a comment we can have incomplete sexps
559 ;; this would have signaled an error.
560 (or (forward-comment (- buf-size)) t); `or' is a hack to
561 ; make it return t
562 (re-search-backward both-regexps (point-min) t))
563 (if (match-beginning 1) ; start of start-regexp
564 (setq done (match-beginning 0))
565 (goto-char (match-end 0)) ; end of end-regexp
566 (funcall hs-forward-sexp-func -1)))
567 (goto-char (or done here))
568 done))
569
570 (defun hs-hide-level-recursive (arg minp maxp)
571 "Hide blocks ARG levels below this block recursively."
572 (when (hs-find-block-beginning)
573 (setq minp (1+ (point)))
574 (forward-sexp)
575 (setq maxp (1- (point))))
576 (hs-flag-region minp maxp ?\n) ; eliminate weirdness
577 (goto-char minp)
578 (while (progn
579 (forward-comment (buffer-size))
580 (re-search-forward hs-block-start-regexp maxp t))
581 (if (> arg 1)
582 (hs-hide-level-recursive (1- arg) minp maxp)
583 (goto-char (match-beginning 0))
584 (hs-hide-block-at-point t)))
585 (hs-safety-is-job-n)
586 (goto-char maxp))
587
588 (defmacro hs-life-goes-on (&rest body)
589 "Execute optional BODY iff variable `hs-minor-mode' is non-nil."
590 `(let ((inhibit-point-motion-hooks t))
591 (when hs-minor-mode
592 ,@body)))
593
594 (put 'hs-life-goes-on 'edebug-form-spec '(&rest form))
595
596 (defun hs-already-hidden-p ()
597 "Return non-nil if point is in an already-hidden block, otherwise nil."
598 (save-excursion
599 (let ((c-reg (hs-inside-comment-p)))
600 (if (and c-reg (nth 0 c-reg))
601 ;; point is inside a comment, and that comment is hidable
602 (goto-char (nth 0 c-reg))
603 (if (and (not c-reg) (hs-find-block-beginning)
604 (looking-at hs-block-start-regexp))
605 ;; point is inside a block
606 (goto-char (match-end 0)))))
607 (end-of-line)
608 (let ((overlays (overlays-at (point)))
609 (found nil))
610 (while (and (not found) (overlayp (car overlays)))
611 (setq found (overlay-get (car overlays) 'hs)
612 overlays (cdr overlays)))
613 found)))
614
615 (defun java-hs-forward-sexp (arg)
616 "Function used by `hs-minor-mode' for `forward-sexp' in Java mode."
617 (if (< arg 0)
618 (backward-sexp 1)
619 (if (looking-at hs-block-start-regexp)
620 (progn
621 (goto-char (match-end 0))
622 (forward-char -1)
623 (forward-sexp 1))
624 (forward-sexp 1))))
625
626 (defun hs-c-like-adjust-block-beginning (arg)
627 "Function to be assigned to `hs-adjust-block-beginning' for C-like modes.
628 Arg is a position in buffer just after {. This goes back to the end of
629 the function header. The purpose is to save some space on the screen
630 when displaying hidden blocks."
631 (save-excursion
632 (goto-char arg)
633 (forward-char -1)
634 (forward-comment (- (buffer-size)))
635 (point)))
636
637 ;;;----------------------------------------------------------------------------
638 ;;; commands
639
640 ;;;###autoload
641 (defun hs-hide-all ()
642 "Hide all top-level blocks, displaying only first and last lines.
643 Move point to the beginning of the line, and it run the normal hook
644 `hs-hide-hook'. See documentation for `run-hooks'.
645 If `hs-hide-comments-when-hiding-all' is t, also hide the comments."
646 (interactive)
647 (hs-life-goes-on
648 (message "Hiding all blocks ...")
649 (save-excursion
650 (hs-flag-region (point-min) (point-max) nil) ; eliminate weirdness
651 (goto-char (point-min))
652 (if hs-hide-comments-when-hiding-all
653 (let (c-reg
654 (count 0)
655 (block-and-comment-re ;; this should match
656 (concat "\\(^" ;; the block beginning and comment start
657 hs-block-start-regexp
658 "\\)\\|\\(" hs-c-start-regexp "\\)")))
659 (while (re-search-forward block-and-comment-re (point-max) t)
660 (if (match-beginning 1) ;; we have found a block beginning
661 (progn
662 (goto-char (match-beginning 1))
663 (hs-hide-block-at-point t)
664 (message "Hiding ... %d" (setq count (1+ count))))
665 ;;found a comment
666 (setq c-reg (hs-inside-comment-p))
667 (if (and c-reg (car c-reg))
668 (if (> (count-lines (car c-reg) (nth 1 c-reg))
669 (if hs-show-hidden-short-form 1 2))
670 (progn
671 (hs-hide-block-at-point t c-reg)
672 (message "Hiding ... %d" (setq count (1+ count))))
673 (goto-char (nth 1 c-reg)))))))
674 (let ((count 0)
675 (top-level-re (concat "^" hs-block-start-regexp))
676 (buf-size (buffer-size)))
677 (while
678 (progn
679 (forward-comment buf-size)
680 (re-search-forward top-level-re (point-max) t))
681 (goto-char (match-beginning 0))
682 (hs-hide-block-at-point t)
683 (message "Hiding ... %d" (setq count (1+ count))))))
684 (hs-safety-is-job-n))
685 (beginning-of-line)
686 (message "Hiding all blocks ... done")
687 (run-hooks 'hs-hide-hook)))
688
689 (defun hs-show-all ()
690 "Show all top-level blocks.
691 Point is unchanged; run the normal hook `hs-show-hook'.
692 See documentation for `run-hooks'."
693 (interactive)
694 (hs-life-goes-on
695 (message "Showing all blocks ...")
696 (hs-flag-region (point-min) (point-max) nil)
697 (message "Showing all blocks ... done")
698 (run-hooks 'hs-show-hook)))
699
700 (defun hs-hide-block (&optional end)
701 "Select a block and hide it.
702 With prefix arg, reposition at end. Block is defined as a sexp for
703 Lispish modes, mode-specific otherwise. Comments are blocks, too.
704 Upon completion, point is repositioned and the normal hook
705 `hs-hide-hook' is run. See documentation for `run-hooks'."
706 (interactive "P")
707 (hs-life-goes-on
708 (let ((c-reg (hs-inside-comment-p)))
709 (cond
710 ((and c-reg (or (null (nth 0 c-reg))
711 (<= (count-lines (car c-reg) (nth 1 c-reg))
712 (if hs-show-hidden-short-form 1 2))))
713 (message "Not enough comment lines to hide!"))
714 ((or c-reg (looking-at hs-block-start-regexp)
715 (hs-find-block-beginning))
716 (hs-hide-block-at-point end c-reg)
717 (hs-safety-is-job-n)
718 (run-hooks 'hs-hide-hook))))))
719
720 (defun hs-show-block (&optional end)
721 "Select a block and show it.
722 With prefix arg, reposition at end. Upon completion, point is
723 repositioned and the normal hook `hs-show-hook' is run.
724 See documentation for `hs-hide-block' and `run-hooks'."
725 (interactive "P")
726 (hs-life-goes-on
727 (let ((c-reg (hs-inside-comment-p)))
728 (if (or c-reg
729 (looking-at hs-block-start-regexp)
730 (hs-find-block-beginning))
731 (progn
732 (hs-show-block-at-point end c-reg)
733 (hs-safety-is-job-n)
734 (run-hooks 'hs-show-hook))))))
735
736 (defun hs-show-region (beg end)
737 "Show all lines from BEG to END, without doing any block analysis.
738 Note: `hs-show-region' is intended for use when `hs-show-block' signals
739 \"unbalanced parentheses\" and so is an emergency measure only. You may
740 become very confused if you use this command indiscriminately."
741 (interactive "r")
742 (hs-life-goes-on
743 (hs-flag-region beg end nil)
744 (hs-safety-is-job-n)
745 (run-hooks 'hs-show-hook)))
746
747 (defun hs-hide-level (arg)
748 "Hide all blocks ARG levels below this block."
749 (interactive "p")
750 (hs-life-goes-on
751 (save-excursion
752 (message "Hiding blocks ...")
753 (hs-hide-level-recursive arg (point-min) (point-max))
754 (message "Hiding blocks ... done"))
755 (hs-safety-is-job-n)
756 (run-hooks 'hs-hide-hook)))
757
758 ;;;###autoload
759 (defun hs-mouse-toggle-hiding (e)
760 "Toggle hiding/showing of a block.
761 Should be bound to a mouse key."
762 (interactive "@e")
763 (mouse-set-point e)
764 (if (hs-already-hidden-p)
765 (hs-show-block)
766 (hs-hide-block)))
767
768 ;;;###autoload
769 (defun hs-minor-mode (&optional arg)
770 "Toggle hideshow minor mode.
771 With ARG, turn hideshow minor mode on if ARG is positive, off otherwise.
772 When hideshow minor mode is on, the menu bar is augmented with hideshow
773 commands and the hideshow commands are enabled.
774 The value '(hs . t) is added to `buffer-invisibility-spec'.
775 Last, the normal hook `hs-minor-mode-hook' is run; see the doc
776 for `run-hooks'.
777
778 The main commands are: `hs-hide-all', `hs-show-all', `hs-hide-block',
779 `hs-show-block', `hs-hide-level' and `hs-show-region'.
780 Also see the documentation for the variable `hs-show-hidden-short-form'.
781
782 Turning hideshow minor mode off reverts the menu bar and the
783 variables to default values and disables the hideshow commands.
784
785 Key bindings:
786 \\{hs-minor-mode-map}"
787
788 (interactive "P")
789 (setq hs-minor-mode
790 (if (null arg)
791 (not hs-minor-mode)
792 (> (prefix-numeric-value arg) 0)))
793 (if hs-minor-mode
794 (progn
795 ; (if (eq hs-emacs-type 'lucid)
796 ; (progn
797 ; (set-buffer-menubar (copy-sequence current-menubar))
798 ; (add-menu nil (car hs-menu-bar) (cdr hs-menu-bar))))
799 (make-local-variable 'line-move-ignore-invisible)
800 (setq line-move-ignore-invisible t)
801 (add-to-invisibility-spec '(hs . t)) ;;hs invisible
802 (hs-grok-mode-type)
803 (run-hooks 'hs-minor-mode-hook))
804 ; (if (eq hs-emacs-type 'lucid)
805 ; (set-buffer-menubar (delete hs-menu-bar current-menubar)))
806 (remove-from-invisibility-spec '(hs . t))))
807
808
809 ;;;----------------------------------------------------------------------------
810 ;;; load-time setup routines
811
812 ;; which emacs being used?
813 ;(setq hs-emacs-type
814 ; (if (string-match "xemacs\\|lucid" emacs-version)
815 ; 'lucid
816 ; 'fsf))
817
818 ;; keymaps and menus
819 (if hs-minor-mode-map
820 nil
821 (setq hs-minor-mode-map (make-sparse-keymap))
822 (easy-menu-define hs-minor-mode-menu
823 hs-minor-mode-map
824 "Menu used when hideshow minor mode is active."
825 (cons "Hide/Show"
826 (mapcar
827 ;; populate keymap then massage entry for easymenu
828 (lambda (ent)
829 (define-key hs-minor-mode-map (aref ent 2) (aref ent 1))
830 (aset ent 2 (not (vectorp (aref ent 2)))) ; disable mousy stuff
831 ent)
832 ;; I believe there is nothing bound on these keys
833 ;; menu entry command key
834 '(["Hide Block" hs-hide-block "\C-ch"]
835 ["Show Block" hs-show-block "\C-cs"]
836 ["Hide All" hs-hide-all "\C-cH"]
837 ["Show All" hs-show-all "\C-cS"]
838 ["Hide Level" hs-hide-level "\C-cL"]
839 ["Show Region" hs-show-region "\C-cR"]
840 ["Toggle Hiding" hs-mouse-toggle-hiding [S-mouse-2]]
841 )))))
842
843 ;; some housekeeping
844 (or (assq 'hs-minor-mode minor-mode-map-alist)
845 (setq minor-mode-map-alist
846 (cons (cons 'hs-minor-mode hs-minor-mode-map)
847 minor-mode-map-alist)))
848 (or (assq 'hs-minor-mode minor-mode-alist)
849 (setq minor-mode-alist (append minor-mode-alist
850 (list '(hs-minor-mode " hs")))))
851
852 ;; make some variables permanently buffer-local
853 (mapcar (lambda (var)
854 (make-variable-buffer-local var)
855 (put var 'permanent-local t))
856 '(hs-minor-mode
857 hs-c-start-regexp
858 hs-block-start-regexp
859 hs-block-end-regexp
860 hs-forward-sexp-func
861 hs-adjust-block-beginning))
862
863 ;;;----------------------------------------------------------------------------
864 ;;; that's it
865
866 (provide 'hideshow)
867
868 ;;; hideshow.el ends here