;;; hideshow.el --- minor mode cmds to selectively display code/comment blocks
-;; Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01 Free Software Foundation
+;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+;; 2004, 2005, 2006 Free Software Foundation, Inc.
;; Author: Thien-Thi Nguyen <ttn@gnu.org>
;; Dan Nicolaescu <dann@ics.uci.edu>
;; Keywords: C C++ java lisp tools editing comments blocks hiding outlines
-;; Maintainer-Version: 5.31
+;; Maintainer-Version: 5.65.2.2
;; Time-of-Day-Author-Most-Likely-to-be-Recalcitrant: early morning
;; This file is part of GNU Emacs.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;
;; (load-library "hideshow")
;; (add-hook 'X-mode-hook ; other modes similarly
-;; '(lambda () (hs-minor-mode 1)))
+;; (lambda () (hs-minor-mode 1)))
;;
;; where X = {emacs-lisp,c,c++,perl,...}. You can also manually toggle
;; hideshow minor mode by typing `M-x hs-minor-mode'. After hideshow is
;; variable `hs-special-modes-alist'. Packages that use hideshow should
;; do something like:
;;
-;; (let ((my-mode-hs-info '(my-mode "{{" "}}" ...)))
-;; (if (not (member my-mode-hs-info hs-special-modes-alist))
-;; (setq hs-special-modes-alist
-;; (cons my-mode-hs-info hs-special-modes-alist))))
+;; (add-to-list 'hs-special-modes-alist '(my-mode "{{" "}}" ...))
;;
;; If you have an entry that works particularly well, consider
;; submitting it for inclusion in hideshow.el. See docstring for
;; `hs-special-modes-alist' for more info on the entry format.
+;;
+;; See also variable `hs-set-up-overlay' for per-block customization of
+;; appearance or other effects associated with overlays. For example:
+;;
+;; (setq hs-set-up-overlay
+;; (defun my-display-code-line-counts (ov)
+;; (when (eq 'code (overlay-get ov 'hs))
+;; (overlay-put ov 'display
+;; (propertize
+;; (format " ... <%d>"
+;; (count-lines (overlay-start ov)
+;; (overlay-end ov)))
+;; 'face 'font-lock-type-face)))))
;; * Bugs
;;
;; In the case of `vc-diff', here is a less invasive workaround:
;;
;; (add-hook 'vc-before-checkin-hook
-;; '(lambda ()
-;; (goto-char (point-min))
-;; (hs-show-block)))
+;; (lambda ()
+;; (goto-char (point-min))
+;; (hs-show-block)))
;;
;; Unfortunately, these workarounds do not restore hideshow state.
;; If someone figures out a better way, please let me know.
;; Thanks go to the following people for valuable ideas, code and
;; bug reports.
;;
-;; Dean Andrews, Alf-Ivar Holm, Holger Bauer, Christoph Conrad, Dave
-;; Love, Dirk Herrmann, Gael Marziou, Jan Djarv, Guillaume Leray,
-;; Moody Ahmad, Preston F. Crow, Lars Lindberg, Reto Zimmermann,
-;; Keith Sheffield, Chew Meng Kuan, Tony Lam, Pete Ware, François
-;; Pinard, Stefan Monnier, Joseph Eydelnant, Michael Ernst
+;; Dean Andrews, Alf-Ivar Holm, Holger Bauer, Christoph Conrad, Dave Love,
+;; Dirk Herrmann, Gael Marziou, Jan Djarv, Guillaume Leray, Moody Ahmad,
+;; Preston F. Crow, Lars Lindberg, Reto Zimmermann, Keith Sheffield,
+;; Chew Meng Kuan, Tony Lam, Pete Ware, François Pinard, Stefan Monnier,
+;; Joseph Eydelnant, Michael Ernst, Peter Heslin
;;
;; Special thanks go to Dan Nicolaescu, who reimplemented hideshow using
;; overlays (rather than selective display), added isearch magic, folded
:prefix "hs-"
:group 'languages)
-;;;###autoload
(defcustom hs-hide-comments-when-hiding-all t
"*Hide the comments too when you do an `hs-hide-all'."
:type 'boolean
'((c-mode "{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning)
(c++-mode "{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning)
(bibtex-mode ("^@\\S(*\\(\\s(\\)" 1))
- (java-mode "{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning)
- )
+ (java-mode "{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning))
"*Alist for initializing the hideshow variables for different modes.
Each element has the form
(MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC).
(defvar hs-hide-all-non-comment-function nil
"*Function called if non-nil when doing `hs-hide-all' for non-comments.")
+(defvar hs-allow-nesting nil
+ "*If non-nil, hiding remembers internal blocks.
+This means that when the outer block is shown again, any
+previously hidden internal blocks remain hidden.")
+
(defvar hs-hide-hook nil
"*Hook called (with `run-hooks') at the end of commands to hide text.
These commands include the toggling commands (when the result is to hide
These commands include the toggling commands (when the result is to show
a block), `hs-show-all' and `hs-show-block'..")
+(defvar hs-set-up-overlay nil
+ "*Function called with one arg, OV, a newly initialized overlay.
+Hideshow puts a unique overlay on each range of text to be hidden
+in the buffer. Here is a simple example of how to use this variable:
+
+ (defun display-code-line-counts (ov)
+ (when (eq 'code (overlay-get ov 'hs))
+ (overlay-put ov 'display
+ (format \"... / %d\"
+ (count-lines (overlay-start ov)
+ (overlay-end ov))))))
+
+ (setq hs-set-up-overlay 'display-code-line-counts)
+
+This example shows how to get information from the overlay as well
+as how to set its `display' property. See `hs-make-overlay' and
+info node `(elisp)Overlays'.")
+
;;---------------------------------------------------------------------------
;; internal variables
;;---------------------------------------------------------------------------
;; system dependency
-; ;; xemacs compatibility
-; (when (string-match "xemacs\\|lucid" emacs-version)
-; ;; use pre-packaged compatiblity layer
-; (require 'overlay))
-;
-; ;; xemacs and emacs-19 compatibility
-; (when (or (not (fboundp 'add-to-invisibility-spec))
-; (not (fboundp 'remove-from-invisibility-spec)))
-; ;; `buffer-invisibility-spec' mutators snarfed from Emacs 20.3 lisp/subr.el
-; (defun add-to-invisibility-spec (arg)
-; (cond
-; ((or (null buffer-invisibility-spec) (eq buffer-invisibility-spec t))
-; (setq buffer-invisibility-spec (list arg)))
-; (t
-; (setq buffer-invisibility-spec
-; (cons arg buffer-invisibility-spec)))))
-; (defun remove-from-invisibility-spec (arg)
-; (when buffer-invisibility-spec
-; (setq buffer-invisibility-spec
-; (delete arg buffer-invisibility-spec)))))
-
-;; hs-match-data
(defalias 'hs-match-data 'match-data)
;;---------------------------------------------------------------------------
;; support functions
(defun hs-discard-overlays (from to)
- "Delete hideshow overlays in region defined by FROM and TO."
+ "Delete hideshow overlays in region defined by FROM and TO.
+Skip \"internal\" overlays if `hs-allow-nesting' is non-nil."
(when (< to from)
(setq from (prog1 to (setq to from))))
- (let ((ovs (overlays-in from to)))
- (while ovs
- (let ((ov (car ovs)))
- (when (overlay-get ov 'hs)
- (delete-overlay ov)))
- (setq ovs (cdr ovs)))))
+ (if hs-allow-nesting
+ (let (ov)
+ (while (> to (setq from (next-overlay-change from)))
+ (when (setq ov (hs-overlay-at from))
+ (setq from (overlay-end ov))
+ (delete-overlay ov))))
+ (dolist (ov (overlays-in from to))
+ (when (overlay-get ov 'hs)
+ (delete-overlay ov)))))
+
+(defun hs-make-overlay (b e kind &optional b-offset e-offset)
+ "Return a new overlay in region defined by B and E with type KIND.
+KIND is either `code' or `comment'. Optional fourth arg B-OFFSET
+when added to B specifies the actual buffer position where the block
+begins. Likewise for optional fifth arg E-OFFSET. If unspecified
+they are taken to be 0 (zero). The following properties are set
+in the overlay: 'invisible 'hs 'hs-b-offset 'hs-e-offset. Also,
+depending on variable `hs-isearch-open', the following properties may
+be present: 'isearch-open-invisible 'isearch-open-invisible-temporary.
+If variable `hs-set-up-overlay' is non-nil it should specify a function
+to call with the newly initialized overlay."
+ (unless b-offset (setq b-offset 0))
+ (unless e-offset (setq e-offset 0))
+ (let ((ov (make-overlay b e))
+ (io (if (eq 'block hs-isearch-open)
+ ;; backward compatibility -- `block'<=>`code'
+ 'code
+ hs-isearch-open)))
+ (overlay-put ov 'invisible 'hs)
+ (overlay-put ov 'hs kind)
+ (overlay-put ov 'hs-b-offset b-offset)
+ (overlay-put ov 'hs-e-offset e-offset)
+ (when (or (eq io t) (eq io kind))
+ (overlay-put ov 'isearch-open-invisible 'hs-isearch-show)
+ (overlay-put ov 'isearch-open-invisible-temporary
+ 'hs-isearch-show-temporary))
+ (when hs-set-up-overlay (funcall hs-set-up-overlay ov))
+ ov))
(defun hs-isearch-show (ov)
"Delete overlay OV, and set `hs-headline' to nil.
This function is meant to be used as the `isearch-open-invisible-temporary'
property of an overlay."
(setq hs-headline
- (if hide-p
- nil
- (or hs-headline
- (let ((start (overlay-start ov)))
- (buffer-substring
- (save-excursion (goto-char start)
- (beginning-of-line)
- (skip-chars-forward " \t")
- (point))
- start)))))
+ (if hide-p
+ nil
+ (or hs-headline
+ (let ((start (overlay-start ov)))
+ (buffer-substring
+ (save-excursion (goto-char start)
+ (beginning-of-line)
+ (skip-chars-forward " \t")
+ (point))
+ start)))))
(force-mode-line-update)
+ ;; handle `display' property specially
+ (let (value)
+ (if hide-p
+ (when (setq value (overlay-get ov 'hs-isearch-display))
+ (overlay-put ov 'display value)
+ (overlay-put ov 'hs-isearch-display nil))
+ (when (setq value (overlay-get ov 'display))
+ (overlay-put ov 'hs-isearch-display value)
+ (overlay-put ov 'display nil))))
(overlay-put ov 'invisible (and hide-p 'hs)))
-(defun hs-flag-region (from to flag)
- "Hide or show lines from FROM to TO, according to FLAG.
-If FLAG is nil then text is shown, while if FLAG is non-nil the text is
-hidden. FLAG must be one of the symbols `code' or `comment', depending
-on what kind of block is to be hidden."
- (save-excursion
- ;; first clear it all out
- (hs-discard-overlays from to)
- ;; now create overlays if needed
- (when flag
- (let ((overlay (make-overlay from to)))
- (overlay-put overlay 'invisible 'hs)
- (overlay-put overlay 'hs flag)
- (when (or (eq hs-isearch-open t)
- (eq hs-isearch-open flag)
- ;; deprecated backward compatibility -- `block'<=>`code'
- (and (eq 'block hs-isearch-open)
- (eq 'code flag)))
- (overlay-put overlay 'isearch-open-invisible 'hs-isearch-show)
- (overlay-put overlay
- 'isearch-open-invisible-temporary
- 'hs-isearch-show-temporary))
- overlay))))
-
(defun hs-forward-sexp (match-data arg)
"Adjust point based on MATCH-DATA and call `hs-forward-sexp-func' w/ ARG.
Original match data is restored upon return."
(defun hs-hide-comment-region (beg end &optional repos-end)
"Hide a region from BEG to END, marking it as a comment.
Optional arg REPOS-END means reposition at end."
- (hs-flag-region (progn (goto-char beg) (end-of-line) (point))
- (progn (goto-char end) (end-of-line) (point))
- 'comment)
+ (let ((beg-eol (progn (goto-char beg) (end-of-line) (point)))
+ (end-eol (progn (goto-char end) (end-of-line) (point))))
+ (hs-discard-overlays beg-eol end-eol)
+ (hs-make-overlay beg-eol end-eol 'comment beg end))
(goto-char (if repos-end end beg)))
(defun hs-hide-block-at-point (&optional end comment-reg)
;; `q' is the point at the end of the block
(progn (hs-forward-sexp mdata 1)
(end-of-line)
- (point))))
+ (point)))
+ ov)
(when (and (< p (point)) (> (count-lines p q) 1))
- (overlay-put (hs-flag-region p q 'code)
- 'hs-ofs
- (- pure-p p)))
+ (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p)))
+ (delete-overlay ov))
+ ((not hs-allow-nesting)
+ (hs-discard-overlays p q)))
+ (hs-make-overlay p q 'code (- pure-p p)))
(goto-char (if end q (min p pure-p)))))))
-(defun hs-safety-is-job-n ()
- "Warn if `buffer-invisibility-spec' does not contain symbol `hs'."
- (unless (and (listp buffer-invisibility-spec)
- (assq 'hs buffer-invisibility-spec))
- (message "Warning: `buffer-invisibility-spec' does not contain hs!!")
- (sit-for 2)))
-
(defun hs-inside-comment-p ()
"Return non-nil if point is inside a comment, otherwise nil.
Actually, return a list containing the buffer position of the start
(let ((q (point)))
(when (or (looking-at hs-c-start-regexp)
(re-search-backward hs-c-start-regexp (point-min) t))
+ ;; first get to the beginning of this comment...
+ (while (and (not (bobp))
+ (= (point) (progn (forward-comment -1) (point))))
+ (forward-char -1))
+ ;; ...then extend backwards
(forward-comment (- (buffer-size)))
(skip-chars-forward " \t\n\f")
(let ((p (point))
- (not-hidable nil))
+ (hidable t))
(beginning-of-line)
(unless (looking-at (concat "[ \t]*" hs-c-start-regexp))
;; we are in this situation: (example)
(while (and (< (point) q)
(> (point) p)
(not (looking-at hs-c-start-regexp)))
- (setq p (point));; use this to avoid an infinite cycle
+ ;; avoid an infinite cycle
+ (setq p (point))
(forward-comment 1)
(skip-chars-forward " \t\n\f"))
(when (or (not (looking-at hs-c-start-regexp))
(> (point) q))
;; we cannot hide this comment block
- (setq not-hidable t)))
+ (setq hidable nil)))
;; goto the end of the comment
(forward-comment (buffer-size))
(skip-chars-backward " \t\n\f")
(end-of-line)
(when (>= (point) q)
- (list (if not-hidable nil p) (point))))))))
+ (list (and hidable p) (point))))))))
(defun hs-grok-mode-type ()
"Set up hideshow variables for new buffers.
(setq minp (1+ (point)))
(funcall hs-forward-sexp-func 1)
(setq maxp (1- (point))))
- (hs-flag-region minp maxp nil) ; eliminate weirdness
+ (unless hs-allow-nesting
+ (hs-discard-overlays minp maxp))
(goto-char minp)
(while (progn
(forward-comment (buffer-size))
(hs-hide-level-recursive (1- arg) minp maxp)
(goto-char (match-beginning hs-block-start-mdata-select))
(hs-hide-block-at-point t)))
- (hs-safety-is-job-n)
(goto-char maxp))
(defmacro hs-life-goes-on (&rest body)
(put 'hs-life-goes-on 'edebug-form-spec '(&rest form))
+(defun hs-overlay-at (position)
+ "Return hideshow overlay at POSITION, or nil if none to be found."
+ (let ((overlays (overlays-at position))
+ ov found)
+ (while (and (not found) (setq ov (car overlays)))
+ (setq found (and (overlay-get ov 'hs) ov)
+ overlays (cdr overlays)))
+ found))
+
(defun hs-already-hidden-p ()
"Return non-nil if point is in an already-hidden block, otherwise nil."
(save-excursion
;; point is inside a block
(goto-char (match-end 0)))))
(end-of-line)
- (let ((overlays (overlays-at (point)))
- (found nil))
- (while (and (not found) (overlayp (car overlays)))
- (setq found (overlay-get (car overlays) 'hs)
- overlays (cdr overlays)))
- found)))
+ (hs-overlay-at (point))))
(defun hs-c-like-adjust-block-beginning (initial)
"Adjust INITIAL, the buffer position after `hs-block-start-regexp'.
(hs-life-goes-on
(message "Hiding all blocks ...")
(save-excursion
- (hs-flag-region (point-min) (point-max) nil) ; eliminate weirdness
+ (unless hs-allow-nesting
+ (hs-discard-overlays (point-min) (point-max)))
(goto-char (point-min))
(let ((count 0)
(re (concat "\\("
(funcall hs-hide-all-non-comment-function)
(hs-hide-block-at-point t)))
;; found a comment, probably
- (let ((c-reg (hs-inside-comment-p))) ; blech!
+ (let ((c-reg (hs-inside-comment-p)))
(when (and c-reg (car c-reg))
(if (> (count-lines (car c-reg) (nth 1 c-reg)) 1)
(hs-hide-block-at-point t c-reg)
(goto-char (nth 1 c-reg))))))
- (message "Hiding ... %d" (setq count (1+ count)))))
- (hs-safety-is-job-n))
+ (message "Hiding ... %d" (setq count (1+ count))))))
(beginning-of-line)
(message "Hiding all blocks ... done")
(run-hooks 'hs-hide-hook)))
(interactive)
(hs-life-goes-on
(message "Showing all blocks ...")
- (hs-flag-region (point-min) (point-max) nil)
+ (let ((hs-allow-nesting nil))
+ (hs-discard-overlays (point-min) (point-max)))
(message "Showing all blocks ... done")
(run-hooks 'hs-show-hook)))
(looking-at hs-block-start-regexp)
(hs-find-block-beginning))
(hs-hide-block-at-point end c-reg)
- (hs-safety-is-job-n)
(run-hooks 'hs-hide-hook))))))
(defun hs-show-block (&optional end)
(hs-life-goes-on
(or
;; first see if we have something at the end of the line
- (catch 'eol-begins-hidden-region-p
- (let ((here (point))
- (ovs (save-excursion (end-of-line) (overlays-at (point)))))
- (while ovs
- (let ((ov (car ovs)))
- (when (overlay-get ov 'hs)
- (goto-char
- (cond (end (overlay-end ov))
- ((eq 'comment (overlay-get ov 'hs)) here)
- (t (+ (overlay-start ov) (overlay-get ov 'hs-ofs)))))
- (delete-overlay ov)
- (throw 'eol-begins-hidden-region-p t)))
- (setq ovs (cdr ovs)))
- nil))
+ (let ((ov (hs-overlay-at (save-excursion (end-of-line) (point))))
+ (here (point)))
+ (when ov
+ (goto-char
+ (cond (end (overlay-end ov))
+ ((eq 'comment (overlay-get ov 'hs)) here)
+ (t (+ (overlay-start ov) (overlay-get ov 'hs-b-offset)))))
+ (delete-overlay ov)
+ t))
;; not immediately obvious, look for a suitable block
(let ((c-reg (hs-inside-comment-p))
p q)
(setq p (car c-reg)
q (cadr c-reg))))
((and (hs-find-block-beginning)
- (looking-at hs-block-start-regexp)) ; fresh match-data, ugh
+ ;; ugh, fresh match-data
+ (looking-at hs-block-start-regexp))
(setq p (point)
q (progn (hs-forward-sexp (hs-match-data t) 1) (point)))))
(when (and p q)
- (hs-flag-region p q nil)
+ (hs-discard-overlays p q)
(goto-char (if end q (1+ p)))))
- (hs-safety-is-job-n)
(run-hooks 'hs-show-hook))))
(defun hs-hide-level (arg)
(message "Hiding blocks ...")
(hs-hide-level-recursive arg (point-min) (point-max))
(message "Hiding blocks ... done"))
- (hs-safety-is-job-n)
(run-hooks 'hs-hide-hook)))
(defun hs-toggle-hiding ()
(interactive "P")
(setq hs-headline nil
- hs-minor-mode (if (null arg)
- (not hs-minor-mode)
- (> (prefix-numeric-value arg) 0)))
+ hs-minor-mode (if (null arg)
+ (not hs-minor-mode)
+ (> (prefix-numeric-value arg) 0)))
(if hs-minor-mode
(progn
(hs-grok-mode-type)
)))))
;; some housekeeping
-(or (assq 'hs-minor-mode minor-mode-map-alist)
- (setq minor-mode-map-alist
- (cons (cons 'hs-minor-mode hs-minor-mode-map)
- minor-mode-map-alist)))
-(or (assq 'hs-minor-mode minor-mode-alist)
- (setq minor-mode-alist (append minor-mode-alist
- (list '(hs-minor-mode " hs")))))
+(add-to-list 'minor-mode-map-alist (cons 'hs-minor-mode hs-minor-mode-map))
+(add-to-list 'minor-mode-alist '(hs-minor-mode " hs") t)
;; make some variables permanently buffer-local
-(let ((vars '(hs-minor-mode
- hs-c-start-regexp
- hs-block-start-regexp
- hs-block-start-mdata-select
- hs-block-end-regexp
- hs-forward-sexp-func
- hs-adjust-block-beginning)))
- (while vars
- (let ((var (car vars)))
- (make-variable-buffer-local var)
- (put var 'permanent-local t))
- (setq vars (cdr vars))))
+(dolist (var '(hs-minor-mode
+ hs-c-start-regexp
+ hs-block-start-regexp
+ hs-block-start-mdata-select
+ hs-block-end-regexp
+ hs-forward-sexp-func
+ hs-adjust-block-beginning))
+ (make-variable-buffer-local var)
+ (put var 'permanent-local t))
;;---------------------------------------------------------------------------
;; that's it