Commit | Line | Data |
---|---|---|
c38e0c97 | 1 | ;;; hideshow.el --- minor mode cmds to selectively display code/comment blocks -*- coding: utf-8 -*- |
6da7653c | 2 | |
ba318903 | 3 | ;; Copyright (C) 1994-2014 Free Software Foundation, Inc. |
b578f267 | 4 | |
9b4a7800 | 5 | ;; Author: Thien-Thi Nguyen <ttn@gnu.org> |
26a0b399 | 6 | ;; Dan Nicolaescu <dann@ics.uci.edu> |
b7c09257 | 7 | ;; Keywords: C C++ java lisp tools editing comments blocks hiding outlines |
a5b101dc | 8 | ;; Maintainer-Version: 5.65.2.2 |
b578f267 EN |
9 | ;; Time-of-Day-Author-Most-Likely-to-be-Recalcitrant: early morning |
10 | ||
11 | ;; This file is part of GNU Emacs. | |
12 | ||
b1fc2b50 | 13 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
b578f267 | 14 | ;; it under the terms of the GNU General Public License as published by |
b1fc2b50 GM |
15 | ;; the Free Software Foundation, either version 3 of the License, or |
16 | ;; (at your option) any later version. | |
b578f267 EN |
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 | |
b1fc2b50 | 24 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
b578f267 | 25 | |
6da7653c TTN |
26 | ;;; Commentary: |
27 | ||
26a0b399 | 28 | ;; * Commands provided |
aaa114d0 | 29 | ;; |
a23c5037 | 30 | ;; This file provides Hideshow Minor Mode. When active, nine commands |
1a8e83dc | 31 | ;; are available, implementing block hiding and showing. They (and their |
26a0b399 | 32 | ;; keybindings) are: |
aaa114d0 | 33 | ;; |
60470e65 TTN |
34 | ;; hs-hide-block C-c @ C-h |
35 | ;; hs-show-block C-c @ C-s | |
36 | ;; hs-hide-all C-c @ C-M-h | |
37 | ;; hs-show-all C-c @ C-M-s | |
38 | ;; hs-hide-level C-c @ C-l | |
39 | ;; hs-toggle-hiding C-c @ C-c | |
26d654ec | 40 | ;; hs-mouse-toggle-hiding [(shift mouse-2)] |
26a0b399 | 41 | ;; hs-hide-initial-comment-block |
b578f267 | 42 | ;; |
26a0b399 TTN |
43 | ;; Blocks are defined per mode. In c-mode, c++-mode and java-mode, they |
44 | ;; are simply text between curly braces, while in Lisp-ish modes parens | |
45 | ;; are used. Multi-line comment blocks can also be hidden. Read-only | |
46 | ;; buffers are not a problem, since hideshow doesn't modify the text. | |
47 | ;; | |
48 | ;; The command `M-x hs-minor-mode' toggles the minor mode or sets it | |
49 | ;; (similar to other minor modes). | |
b578f267 | 50 | |
26d654ec TTN |
51 | ;; * Suggested usage |
52 | ;; | |
53 | ;; First make sure hideshow.el is in a directory in your `load-path'. | |
54 | ;; You can optionally byte-compile it using `M-x byte-compile-file'. | |
865fe16f | 55 | ;; Then, add the following to your init file: |
26d654ec TTN |
56 | ;; |
57 | ;; (load-library "hideshow") | |
58 | ;; (add-hook 'X-mode-hook ; other modes similarly | |
aa7d6700 | 59 | ;; (lambda () (hs-minor-mode 1))) |
26d654ec TTN |
60 | ;; |
61 | ;; where X = {emacs-lisp,c,c++,perl,...}. You can also manually toggle | |
62 | ;; hideshow minor mode by typing `M-x hs-minor-mode'. After hideshow is | |
63 | ;; activated or deactivated, `hs-minor-mode-hook' is run w/ `run-hooks'. | |
64 | ;; | |
65 | ;; Additionally, Joseph Eydelnant writes: | |
66 | ;; I enjoy your package hideshow.el Ver. 5.24 2001/02/13 | |
67 | ;; a lot and I've been looking for the following functionality: | |
68 | ;; toggle hide/show all with a single key. | |
69 | ;; Here are a few lines of code that lets me do just that. | |
70 | ;; | |
71 | ;; (defvar my-hs-hide nil "Current state of hideshow for toggling all.") | |
72 | ;; ;;;###autoload | |
73 | ;; (defun my-toggle-hideshow-all () "Toggle hideshow all." | |
74 | ;; (interactive) | |
75 | ;; (setq my-hs-hide (not my-hs-hide)) | |
76 | ;; (if my-hs-hide | |
77 | ;; (hs-hide-all) | |
78 | ;; (hs-show-all))) | |
79 | ;; | |
80 | ;; [Your hideshow hacks here!] | |
81 | ||
26a0b399 TTN |
82 | ;; * Customization |
83 | ;; | |
84 | ;; You can use `M-x customize-variable' on the following variables: | |
85 | ;; | |
9b4a7800 TTN |
86 | ;; - hs-hide-comments-when-hiding-all -- self-explanatory! |
87 | ;; - hs-hide-all-non-comment-function -- if non-nil, when doing a | |
88 | ;; `hs-hide-all', this function | |
89 | ;; is called w/ no arguments | |
90 | ;; - hs-isearch-open -- what kind of hidden blocks to | |
26a0b399 TTN |
91 | ;; open when doing isearch |
92 | ;; | |
9b4a7800 TTN |
93 | ;; Some languages (e.g., Java) are deeply nested, so the normal behavior |
94 | ;; of `hs-hide-all' (hiding all but top-level blocks) results in very | |
95 | ;; little information shown, which is not very useful. You can use the | |
96 | ;; variable `hs-hide-all-non-comment-function' to implement your idea of | |
97 | ;; what is more useful. For example, the following code shows the next | |
98 | ;; nested level in addition to the top-level: | |
99 | ;; | |
100 | ;; (defun ttn-hs-hide-level-1 () | |
101 | ;; (hs-hide-level 1) | |
102 | ;; (forward-sexp 1)) | |
103 | ;; (setq hs-hide-all-non-comment-function 'ttn-hs-hide-level-1) | |
104 | ;; | |
26a0b399 TTN |
105 | ;; Hideshow works w/ incremental search (isearch) by setting the variable |
106 | ;; `hs-headline', which is the line of text at the beginning of a hidden | |
107 | ;; block that contains a match for the search. You can have this show up | |
108 | ;; in the mode line by modifying the variable `mode-line-format'. For | |
109 | ;; example, the following code prepends this info to the mode line: | |
aaa114d0 | 110 | ;; |
26a0b399 TTN |
111 | ;; (unless (memq 'hs-headline mode-line-format) |
112 | ;; (setq mode-line-format | |
113 | ;; (append '("-" hs-headline) mode-line-format))) | |
aaa114d0 | 114 | ;; |
26a0b399 | 115 | ;; See documentation for `mode-line-format' for more info. |
aaa114d0 TTN |
116 | ;; |
117 | ;; Hooks are run after some commands: | |
118 | ;; | |
119 | ;; hs-hide-hook in hs-hide-block, hs-hide-all, hs-hide-level | |
9b4a7800 | 120 | ;; hs-show-hook hs-show-block, hs-show-all |
aaa114d0 | 121 | ;; |
9b4a7800 TTN |
122 | ;; One of `hs-hide-hook' or `hs-show-hook' is run for the toggling |
123 | ;; commands when the result of the toggle is to hide or show blocks, | |
124 | ;; respectively. All hooks are run w/ `run-hooks'. See docs for each | |
125 | ;; variable or hook for more info. | |
26a0b399 TTN |
126 | ;; |
127 | ;; Normally, hideshow tries to determine appropriate values for block | |
128 | ;; and comment definitions by examining the buffer's major mode. If | |
129 | ;; there are problems, hideshow will not activate and in that case you | |
130 | ;; may wish to override hideshow's heuristics by adding an entry to | |
131 | ;; variable `hs-special-modes-alist'. Packages that use hideshow should | |
132 | ;; do something like: | |
133 | ;; | |
aa7d6700 | 134 | ;; (add-to-list 'hs-special-modes-alist '(my-mode "{{" "}}" ...)) |
26a0b399 TTN |
135 | ;; |
136 | ;; If you have an entry that works particularly well, consider | |
137 | ;; submitting it for inclusion in hideshow.el. See docstring for | |
138 | ;; `hs-special-modes-alist' for more info on the entry format. | |
dfdc1af2 TTN |
139 | ;; |
140 | ;; See also variable `hs-set-up-overlay' for per-block customization of | |
141 | ;; appearance or other effects associated with overlays. For example: | |
142 | ;; | |
143 | ;; (setq hs-set-up-overlay | |
144 | ;; (defun my-display-code-line-counts (ov) | |
145 | ;; (when (eq 'code (overlay-get ov 'hs)) | |
146 | ;; (overlay-put ov 'display | |
147 | ;; (propertize | |
148 | ;; (format " ... <%d>" | |
149 | ;; (count-lines (overlay-start ov) | |
150 | ;; (overlay-end ov))) | |
151 | ;; 'face 'font-lock-type-face))))) | |
b578f267 | 152 | |
26a0b399 TTN |
153 | ;; * Bugs |
154 | ;; | |
155 | ;; (1) Hideshow does not work w/ emacs 18 because emacs 18 lacks the | |
156 | ;; function `forward-comment' (among other things). If someone | |
157 | ;; writes this, please send me a copy. | |
158 | ;; | |
159 | ;; (2) Sometimes `hs-headline' can become out of sync. To reset, type | |
26d654ec | 160 | ;; `M-x hs-minor-mode' twice (that is, deactivate then re-activate |
26a0b399 | 161 | ;; hideshow). |
aaa114d0 | 162 | ;; |
26d654ec | 163 | ;; (3) Hideshow 5.x is developed and tested on GNU Emacs 20.7. |
26a0b399 | 164 | ;; XEmacs compatibility may have bitrotted since 4.29. |
aaa114d0 | 165 | ;; |
9b4a7800 TTN |
166 | ;; (4) Some buffers can't be `byte-compile-file'd properly. This is because |
167 | ;; `byte-compile-file' inserts the file to be compiled in a temporary | |
168 | ;; buffer and switches `normal-mode' on. In the case where you have | |
169 | ;; `hs-hide-initial-comment-block' in `hs-minor-mode-hook', the hiding of | |
170 | ;; the initial comment sometimes hides parts of the first statement (seems | |
171 | ;; to be only in `normal-mode'), so there are unbalanced "(" and ")". | |
172 | ;; | |
173 | ;; The workaround is to clear `hs-minor-mode-hook' when byte-compiling: | |
174 | ;; | |
175 | ;; (defadvice byte-compile-file (around | |
176 | ;; byte-compile-file-hideshow-off | |
177 | ;; act) | |
178 | ;; (let ((hs-minor-mode-hook nil)) | |
179 | ;; ad-do-it)) | |
26d654ec TTN |
180 | ;; |
181 | ;; (5) Hideshow interacts badly with Ediff and `vc-diff'. At the moment, the | |
182 | ;; suggested workaround is to turn off hideshow entirely, for example: | |
183 | ;; | |
26d654ec TTN |
184 | ;; (add-hook 'ediff-prepare-buffer-hook 'turn-off-hideshow) |
185 | ;; (add-hook 'vc-before-checkin-hook 'turn-off-hideshow) | |
186 | ;; | |
187 | ;; In the case of `vc-diff', here is a less invasive workaround: | |
188 | ;; | |
189 | ;; (add-hook 'vc-before-checkin-hook | |
aa7d6700 TTN |
190 | ;; (lambda () |
191 | ;; (goto-char (point-min)) | |
192 | ;; (hs-show-block))) | |
26d654ec TTN |
193 | ;; |
194 | ;; Unfortunately, these workarounds do not restore hideshow state. | |
195 | ;; If someone figures out a better way, please let me know. | |
9b4a7800 | 196 | |
ee7683eb | 197 | ;; * Correspondence |
26d654ec | 198 | ;; |
ee7683eb | 199 | ;; Correspondence welcome; please indicate version number. Send bug |
9b4a7800 | 200 | ;; reports and inquiries to <ttn@gnu.org>. |
b578f267 | 201 | |
26a0b399 | 202 | ;; * Thanks |
aaa114d0 | 203 | ;; |
26a0b399 TTN |
204 | ;; Thanks go to the following people for valuable ideas, code and |
205 | ;; bug reports. | |
aaa114d0 | 206 | ;; |
2bbf1842 TTN |
207 | ;; Dean Andrews, Alf-Ivar Holm, Holger Bauer, Christoph Conrad, Dave Love, |
208 | ;; Dirk Herrmann, Gael Marziou, Jan Djarv, Guillaume Leray, Moody Ahmad, | |
209 | ;; Preston F. Crow, Lars Lindberg, Reto Zimmermann, Keith Sheffield, | |
c38e0c97 | 210 | ;; Chew Meng Kuan, Tony Lam, Pete Ware, François Pinard, Stefan Monnier, |
2bbf1842 | 211 | ;; Joseph Eydelnant, Michael Ernst, Peter Heslin |
aaa114d0 | 212 | ;; |
9b4a7800 TTN |
213 | ;; Special thanks go to Dan Nicolaescu, who reimplemented hideshow using |
214 | ;; overlays (rather than selective display), added isearch magic, folded | |
215 | ;; in custom.el compatibility, generalized comment handling, incorporated | |
216 | ;; mouse support, and maintained the code in general. Version 4.0 is | |
217 | ;; largely due to his efforts. | |
26a0b399 TTN |
218 | |
219 | ;; * History | |
220 | ;; | |
221 | ;; Hideshow was inspired when I learned about selective display. It was | |
222 | ;; reimplemented to use overlays for 4.0 (see above). WRT older history, | |
223 | ;; entries in the masterfile corresponding to versions 1.x and 2.x have | |
224 | ;; been lost. XEmacs support is reliable as of 4.29. State save and | |
225 | ;; restore was added in 3.5 (not widely distributed), and reliable as of | |
226 | ;; 4.30. Otherwise, the code seems stable. Passes checkdoc as of 4.32. | |
227 | ;; Version 5.x uses new algorithms for block selection and traversal, | |
228 | ;; unbundles state save and restore, and includes more isearch support. | |
6da7653c TTN |
229 | |
230 | ;;; Code: | |
231 | ||
26a0b399 TTN |
232 | ;;--------------------------------------------------------------------------- |
233 | ;; user-configurable variables | |
6da7653c | 234 | |
b7c09257 RS |
235 | (defgroup hideshow nil |
236 | "Minor mode for hiding and showing program and comment blocks." | |
12e36cdb | 237 | :prefix "hs-" |
b7c09257 RS |
238 | :group 'languages) |
239 | ||
aaa114d0 | 240 | (defcustom hs-hide-comments-when-hiding-all t |
fb7ada5f | 241 | "Hide the comments too when you do an `hs-hide-all'." |
b7c09257 RS |
242 | :type 'boolean |
243 | :group 'hideshow) | |
244 | ||
26a0b399 | 245 | (defcustom hs-minor-mode-hook nil |
fb7ada5f | 246 | "Hook called when hideshow minor mode is activated or deactivated." |
39f2ec46 | 247 | :type 'hook |
82dab959 TTN |
248 | :group 'hideshow |
249 | :version "21.1") | |
b7c09257 | 250 | |
26d654ec | 251 | (defcustom hs-isearch-open 'code |
fb7ada5f | 252 | "What kind of hidden blocks to open when doing `isearch'. |
26a0b399 | 253 | One of the following symbols: |
aaa114d0 | 254 | |
26d654ec TTN |
255 | code -- open only code blocks |
256 | comment -- open only comment blocks | |
257 | t -- open both code and comment blocks | |
258 | nil -- open neither code nor comment blocks | |
aaa114d0 | 259 | |
e7f767c2 | 260 | This has effect only if `search-invisible' is set to `open'." |
26d654ec TTN |
261 | :type '(choice (const :tag "open only code blocks" code) |
262 | (const :tag "open only comment blocks" comment) | |
263 | (const :tag "open both code and comment blocks" t) | |
26a0b399 | 264 | (const :tag "don't open any of them" nil)) |
12e36cdb RS |
265 | :group 'hideshow) |
266 | ||
23d93b6a | 267 | ;;;###autoload |
aaa114d0 | 268 | (defvar hs-special-modes-alist |
6bdad9ae | 269 | (mapcar 'purecopy |
1c292fc7 CY |
270 | '((c-mode "{" "}" "/[*/]" nil nil) |
271 | (c++-mode "{" "}" "/[*/]" nil nil) | |
5fe5075b | 272 | (bibtex-mode ("@\\S(*\\(\\s(\\)" 1)) |
17b5d0f7 | 273 | (java-mode "{" "}" "/[*/]" nil nil) |
6bdad9ae | 274 | (js-mode "{" "}" "/[*/]" nil))) |
fb7ada5f | 275 | "Alist for initializing the hideshow variables for different modes. |
26a0b399 | 276 | Each element has the form |
aaa114d0 | 277 | (MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC). |
aaa114d0 | 278 | |
26a0b399 TTN |
279 | If non-nil, hideshow will use these values as regexps to define blocks |
280 | and comments, respectively for major mode MODE. | |
281 | ||
282 | START, END and COMMENT-START are regular expressions. A block is | |
283 | defined as text surrounded by START and END. | |
284 | ||
285 | As a special case, START may be a list of the form (COMPLEX-START | |
286 | MDATA-SELECTOR), where COMPLEX-START is a regexp w/ multiple parts and | |
287 | MDATA-SELECTOR an integer that specifies which sub-match is the proper | |
90d606ce TTN |
288 | place to adjust point, before calling `hs-forward-sexp-func'. Point |
289 | is adjusted to the beginning of the specified match. For example, | |
290 | see the `hs-special-modes-alist' entry for `bibtex-mode'. | |
88039caa | 291 | |
26a0b399 TTN |
292 | For some major modes, `forward-sexp' does not work properly. In those |
293 | cases, FORWARD-SEXP-FUNC specifies another function to use instead. | |
294 | ||
295 | See the documentation for `hs-adjust-block-beginning' to see what is the | |
296 | use of ADJUST-BEG-FUNC. | |
297 | ||
298 | If any of the elements is left nil or omitted, hideshow tries to guess | |
299 | appropriate values. The regexps should not contain leading or trailing | |
300 | whitespace. Case does not matter.") | |
6da7653c | 301 | |
9b4a7800 | 302 | (defvar hs-hide-all-non-comment-function nil |
fb7ada5f | 303 | "Function called if non-nil when doing `hs-hide-all' for non-comments.") |
9b4a7800 | 304 | |
a5b101dc | 305 | (defvar hs-allow-nesting nil |
fb7ada5f | 306 | "If non-nil, hiding remembers internal blocks. |
d88444f2 JB |
307 | This means that when the outer block is shown again, |
308 | any previously hidden internal blocks remain hidden.") | |
a5b101dc | 309 | |
d877f247 | 310 | (defvar hs-hide-hook nil |
fb7ada5f | 311 | "Hook called (with `run-hooks') at the end of commands to hide text. |
9b4a7800 TTN |
312 | These commands include the toggling commands (when the result is to hide |
313 | a block), `hs-hide-all', `hs-hide-block' and `hs-hide-level'.") | |
6da7653c | 314 | |
d877f247 | 315 | (defvar hs-show-hook nil |
fb7ada5f | 316 | "Hook called (with `run-hooks') at the end of commands to show text. |
9b4a7800 | 317 | These commands include the toggling commands (when the result is to show |
d88444f2 | 318 | a block), `hs-show-all' and `hs-show-block'.") |
6da7653c | 319 | |
dfdc1af2 | 320 | (defvar hs-set-up-overlay nil |
fb7ada5f | 321 | "Function called with one arg, OV, a newly initialized overlay. |
dfdc1af2 TTN |
322 | Hideshow puts a unique overlay on each range of text to be hidden |
323 | in the buffer. Here is a simple example of how to use this variable: | |
324 | ||
325 | (defun display-code-line-counts (ov) | |
326 | (when (eq 'code (overlay-get ov 'hs)) | |
327 | (overlay-put ov 'display | |
328 | (format \"... / %d\" | |
329 | (count-lines (overlay-start ov) | |
330 | (overlay-end ov)))))) | |
331 | ||
332 | (setq hs-set-up-overlay 'display-code-line-counts) | |
333 | ||
334 | This example shows how to get information from the overlay as well | |
335 | as how to set its `display' property. See `hs-make-overlay' and | |
336 | info node `(elisp)Overlays'.") | |
337 | ||
26a0b399 TTN |
338 | ;;--------------------------------------------------------------------------- |
339 | ;; internal variables | |
6da7653c TTN |
340 | |
341 | (defvar hs-minor-mode nil | |
c1ff6dac | 342 | "Non-nil if using hideshow mode as a minor mode of some other mode. |
26a0b399 | 343 | Use the command `hs-minor-mode' to toggle or set this variable.") |
6da7653c | 344 | |
1f344760 DN |
345 | (defvar hs-minor-mode-map |
346 | (let ((map (make-sparse-keymap))) | |
347 | ;; These bindings roughly imitate those used by Outline mode. | |
348 | (define-key map "\C-c@\C-h" 'hs-hide-block) | |
349 | (define-key map "\C-c@\C-s" 'hs-show-block) | |
350 | (define-key map "\C-c@\C-\M-h" 'hs-hide-all) | |
351 | (define-key map "\C-c@\C-\M-s" 'hs-show-all) | |
352 | (define-key map "\C-c@\C-l" 'hs-hide-level) | |
353 | (define-key map "\C-c@\C-c" 'hs-toggle-hiding) | |
354 | (define-key map [(shift mouse-2)] 'hs-mouse-toggle-hiding) | |
1f344760 | 355 | map) |
26a0b399 | 356 | "Keymap for hideshow minor mode.") |
6da7653c | 357 | |
d79dd1b0 DN |
358 | (easy-menu-define hs-minor-mode-menu hs-minor-mode-map |
359 | "Menu used when hideshow minor mode is active." | |
360 | '("Hide/Show" | |
361 | ["Hide Block" hs-hide-block | |
362 | :help "Hide the code or comment block at point"] | |
363 | ["Show Block" hs-show-block | |
364 | :help "Show the code or comment block at point"] | |
365 | ["Hide All" hs-hide-all | |
366 | :help "Hide all the blocks in the buffer"] | |
367 | ["Show All" hs-show-all | |
76cdeb7d | 368 | :help "Show all the blocks in the buffer"] |
d79dd1b0 DN |
369 | ["Hide Level" hs-hide-level |
370 | :help "Hide all block at levels below the current block"] | |
371 | ["Toggle Hiding" hs-toggle-hiding | |
76cdeb7d DN |
372 | :help "Toggle the hiding state of the current block"] |
373 | "----" | |
fb652bb5 | 374 | ["Hide comments when hiding all" |
76cdeb7d DN |
375 | (setq hs-hide-comments-when-hiding-all |
376 | (not hs-hide-comments-when-hiding-all)) | |
377 | :help "If t also hide comment blocks when doing `hs-hide-all'" | |
378 | :style toggle :selected hs-hide-comments-when-hiding-all] | |
379 | ("Reveal on isearch" | |
380 | ["Code blocks" (setq hs-isearch-open 'code) | |
381 | :help "Show hidden code blocks when isearch matches inside them" | |
382 | :active t :style radio :selected (eq hs-isearch-open 'code)] | |
383 | ["Comment blocks" (setq hs-isearch-open 'comment) | |
384 | :help "Show hidden comment blocks when isearch matches inside them" | |
385 | :active t :style radio :selected (eq hs-isearch-open 'comment)] | |
386 | ["Code and Comment blocks" (setq hs-isearch-open t) | |
387 | :help "Show both hidden code and comment blocks when isearch matches inside them" | |
5c842d76 | 388 | :active t :style radio :selected (eq hs-isearch-open t)] |
76cdeb7d DN |
389 | ["None" (setq hs-isearch-open nil) |
390 | :help "Do not hidden code or comment blocks when isearch matches inside them" | |
391 | :active t :style radio :selected (eq hs-isearch-open nil)]))) | |
d79dd1b0 | 392 | |
6da7653c | 393 | (defvar hs-c-start-regexp nil |
aaa114d0 TTN |
394 | "Regexp for beginning of comments. |
395 | Differs from mode-specific comment regexps in that | |
9479d258 | 396 | surrounding whitespace is stripped.") |
1f344760 | 397 | (make-variable-buffer-local 'hs-c-start-regexp) |
6da7653c | 398 | |
6da7653c | 399 | (defvar hs-block-start-regexp nil |
9479d258 | 400 | "Regexp for beginning of block.") |
1f344760 | 401 | (make-variable-buffer-local 'hs-block-start-regexp) |
6da7653c | 402 | |
26a0b399 TTN |
403 | (defvar hs-block-start-mdata-select nil |
404 | "Element in `hs-block-start-regexp' match data to consider as block start. | |
405 | The internal function `hs-forward-sexp' moves point to the beginning of this | |
406 | element (using `match-beginning') before calling `hs-forward-sexp-func'.") | |
1f344760 | 407 | (make-variable-buffer-local 'hs-block-start-mdata-select) |
26a0b399 | 408 | |
6da7653c | 409 | (defvar hs-block-end-regexp nil |
9479d258 | 410 | "Regexp for end of block.") |
d30aca1b DN |
411 | (make-variable-buffer-local 'hs-block-end-regexp) |
412 | ||
6da7653c TTN |
413 | |
414 | (defvar hs-forward-sexp-func 'forward-sexp | |
aaa114d0 | 415 | "Function used to do a `forward-sexp'. |
9479d258 RS |
416 | Should change for Algol-ish modes. For single-character block |
417 | delimiters -- ie, the syntax table regexp for the character is | |
aaa114d0 TTN |
418 | either `(' or `)' -- `hs-forward-sexp-func' would just be |
419 | `forward-sexp'. For other modes such as simula, a more specialized | |
420 | function is necessary.") | |
1f344760 | 421 | (make-variable-buffer-local 'hs-forward-sexp-func) |
6da7653c | 422 | |
88039caa RS |
423 | (defvar hs-adjust-block-beginning nil |
424 | "Function used to tweak the block beginning. | |
26a0b399 TTN |
425 | The block is hidden from the position returned by this function, |
426 | as opposed to hiding it from the position returned when searching | |
427 | for `hs-block-start-regexp'. | |
428 | ||
429 | For example, in c-like modes, if we wish to also hide the curly braces | |
c98cd8bb | 430 | \(if you think they occupy too much space on the screen), this function |
26a0b399 TTN |
431 | should return the starting point (at the end of line) of the hidden |
432 | region. | |
88039caa | 433 | |
e7bdfdcf | 434 | It is called with a single argument ARG which is the position in |
88039caa RS |
435 | buffer after the block beginning. |
436 | ||
437 | It should return the position from where we should start hiding. | |
438 | ||
aaa114d0 | 439 | It should not move the point. |
88039caa | 440 | |
d06970e5 | 441 | See `hs-c-like-adjust-block-beginning' for an example of using this.") |
1f344760 | 442 | (make-variable-buffer-local 'hs-adjust-block-beginning) |
c1ff6dac | 443 | |
26a0b399 TTN |
444 | (defvar hs-headline nil |
445 | "Text of the line where a hidden block begins, set during isearch. | |
446 | You can display this in the mode line by adding the symbol `hs-headline' | |
447 | to the variable `mode-line-format'. For example, | |
448 | ||
449 | (unless (memq 'hs-headline mode-line-format) | |
450 | (setq mode-line-format | |
451 | (append '(\"-\" hs-headline) mode-line-format))) | |
452 | ||
453 | Note that `mode-line-format' is buffer-local.") | |
454 | ||
26a0b399 TTN |
455 | ;;--------------------------------------------------------------------------- |
456 | ;; support functions | |
457 | ||
458 | (defun hs-discard-overlays (from to) | |
a5b101dc TTN |
459 | "Delete hideshow overlays in region defined by FROM and TO. |
460 | Skip \"internal\" overlays if `hs-allow-nesting' is non-nil." | |
26a0b399 TTN |
461 | (when (< to from) |
462 | (setq from (prog1 to (setq to from)))) | |
a5b101dc TTN |
463 | (if hs-allow-nesting |
464 | (let (ov) | |
465 | (while (> to (setq from (next-overlay-change from))) | |
466 | (when (setq ov (hs-overlay-at from)) | |
467 | (setq from (overlay-end ov)) | |
468 | (delete-overlay ov)))) | |
469 | (dolist (ov (overlays-in from to)) | |
470 | (when (overlay-get ov 'hs) | |
471 | (delete-overlay ov))))) | |
26a0b399 | 472 | |
dfdc1af2 TTN |
473 | (defun hs-make-overlay (b e kind &optional b-offset e-offset) |
474 | "Return a new overlay in region defined by B and E with type KIND. | |
475 | KIND is either `code' or `comment'. Optional fourth arg B-OFFSET | |
476 | when added to B specifies the actual buffer position where the block | |
477 | begins. Likewise for optional fifth arg E-OFFSET. If unspecified | |
478 | they are taken to be 0 (zero). The following properties are set | |
479 | in the overlay: 'invisible 'hs 'hs-b-offset 'hs-e-offset. Also, | |
480 | depending on variable `hs-isearch-open', the following properties may | |
481 | be present: 'isearch-open-invisible 'isearch-open-invisible-temporary. | |
482 | If variable `hs-set-up-overlay' is non-nil it should specify a function | |
483 | to call with the newly initialized overlay." | |
484 | (unless b-offset (setq b-offset 0)) | |
485 | (unless e-offset (setq e-offset 0)) | |
486 | (let ((ov (make-overlay b e)) | |
487 | (io (if (eq 'block hs-isearch-open) | |
488 | ;; backward compatibility -- `block'<=>`code' | |
489 | 'code | |
490 | hs-isearch-open))) | |
491 | (overlay-put ov 'invisible 'hs) | |
492 | (overlay-put ov 'hs kind) | |
493 | (overlay-put ov 'hs-b-offset b-offset) | |
494 | (overlay-put ov 'hs-e-offset e-offset) | |
495 | (when (or (eq io t) (eq io kind)) | |
496 | (overlay-put ov 'isearch-open-invisible 'hs-isearch-show) | |
497 | (overlay-put ov 'isearch-open-invisible-temporary | |
498 | 'hs-isearch-show-temporary)) | |
499 | (when hs-set-up-overlay (funcall hs-set-up-overlay ov)) | |
500 | ov)) | |
501 | ||
26a0b399 | 502 | (defun hs-isearch-show (ov) |
1a8e83dc TTN |
503 | "Delete overlay OV, and set `hs-headline' to nil. |
504 | ||
505 | This function is meant to be used as the `isearch-open-invisible' | |
506 | property of an overlay." | |
26a0b399 | 507 | (setq hs-headline nil) |
1a8e83dc | 508 | (delete-overlay ov)) |
26a0b399 TTN |
509 | |
510 | (defun hs-isearch-show-temporary (ov hide-p) | |
1a8e83dc TTN |
511 | "Hide or show overlay OV, and set `hs-headline', all depending on HIDE-P. |
512 | If HIDE-P is non-nil, `hs-headline' is set to nil and overlay OV is hidden. | |
513 | Otherwise, `hs-headline' is set to the line of text at the head of OV, and | |
514 | OV is shown. | |
515 | ||
516 | This function is meant to be used as the `isearch-open-invisible-temporary' | |
517 | property of an overlay." | |
26a0b399 | 518 | (setq hs-headline |
53c40303 TTN |
519 | (if hide-p |
520 | nil | |
521 | (or hs-headline | |
522 | (let ((start (overlay-start ov))) | |
523 | (buffer-substring | |
524 | (save-excursion (goto-char start) | |
525 | (beginning-of-line) | |
526 | (skip-chars-forward " \t") | |
527 | (point)) | |
528 | start))))) | |
26a0b399 | 529 | (force-mode-line-update) |
dfdc1af2 TTN |
530 | ;; handle `display' property specially |
531 | (let (value) | |
532 | (if hide-p | |
533 | (when (setq value (overlay-get ov 'hs-isearch-display)) | |
534 | (overlay-put ov 'display value) | |
535 | (overlay-put ov 'hs-isearch-display nil)) | |
536 | (when (setq value (overlay-get ov 'display)) | |
537 | (overlay-put ov 'hs-isearch-display value) | |
538 | (overlay-put ov 'display nil)))) | |
26a0b399 | 539 | (overlay-put ov 'invisible (and hide-p 'hs))) |
6da7653c | 540 | |
5012f24c DK |
541 | (defun hs-looking-at-block-start-p () |
542 | "Return non-nil if the point is at the block start." | |
543 | (and (looking-at hs-block-start-regexp) | |
bfeef8b6 | 544 | (save-match-data (not (nth 8 (syntax-ppss)))))) |
5012f24c | 545 | |
26a0b399 TTN |
546 | (defun hs-forward-sexp (match-data arg) |
547 | "Adjust point based on MATCH-DATA and call `hs-forward-sexp-func' w/ ARG. | |
548 | Original match data is restored upon return." | |
549 | (save-match-data | |
550 | (set-match-data match-data) | |
551 | (goto-char (match-beginning hs-block-start-mdata-select)) | |
552 | (funcall hs-forward-sexp-func arg))) | |
553 | ||
554 | (defun hs-hide-comment-region (beg end &optional repos-end) | |
555 | "Hide a region from BEG to END, marking it as a comment. | |
556 | Optional arg REPOS-END means reposition at end." | |
77a2bb53 TTN |
557 | (let ((beg-eol (progn (goto-char beg) (line-end-position))) |
558 | (end-eol (progn (goto-char end) (line-end-position)))) | |
dfdc1af2 TTN |
559 | (hs-discard-overlays beg-eol end-eol) |
560 | (hs-make-overlay beg-eol end-eol 'comment beg end)) | |
26a0b399 | 561 | (goto-char (if repos-end end beg))) |
9479d258 | 562 | |
88039caa | 563 | (defun hs-hide-block-at-point (&optional end comment-reg) |
e7f767c2 | 564 | "Hide block if on block beginning. |
aaa114d0 | 565 | Optional arg END means reposition at end. |
26a0b399 | 566 | Optional arg COMMENT-REG is a list of the form (BEGIN END) and |
aaa114d0 | 567 | specifies the limits of the comment, or nil if the block is not |
26a0b399 TTN |
568 | a comment. |
569 | ||
570 | The block beginning is adjusted by `hs-adjust-block-beginning' | |
571 | and then further adjusted to be at the end of the line." | |
88039caa | 572 | (if comment-reg |
26a0b399 | 573 | (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) |
5012f24c | 574 | (when (hs-looking-at-block-start-p) |
e02f48d7 JB |
575 | (let ((mdata (match-data t)) |
576 | (header-end (match-end 0)) | |
577 | p q ov) | |
1c292fc7 CY |
578 | ;; `p' is the point at the end of the block beginning, which |
579 | ;; may need to be adjusted | |
580 | (save-excursion | |
581 | (if hs-adjust-block-beginning | |
582 | (goto-char (funcall hs-adjust-block-beginning | |
583 | header-end)) | |
584 | (goto-char header-end)) | |
585 | (setq p (line-end-position))) | |
586 | ;; `q' is the point at the end of the block | |
587 | (hs-forward-sexp mdata 1) | |
588 | (setq q (if (looking-back hs-block-end-regexp) | |
589 | (match-beginning 0) | |
590 | (point))) | |
591 | (when (and (< p q) (> (count-lines p q) 1)) | |
a5b101dc TTN |
592 | (cond ((and hs-allow-nesting (setq ov (hs-overlay-at p))) |
593 | (delete-overlay ov)) | |
594 | ((not hs-allow-nesting) | |
595 | (hs-discard-overlays p q))) | |
d44d05e8 CY |
596 | (hs-make-overlay p q 'code (- header-end p))) |
597 | (goto-char (if end q (min p header-end))))))) | |
6da7653c | 598 | |
6da7653c | 599 | (defun hs-inside-comment-p () |
aaa114d0 | 600 | "Return non-nil if point is inside a comment, otherwise nil. |
26a0b399 | 601 | Actually, return a list containing the buffer position of the start |
aaa114d0 TTN |
602 | and the end of the comment. A comment block can be hidden only if on |
603 | its starting line there is only whitespace preceding the actual comment | |
604 | beginning. If we are inside of a comment but this condition is not met, | |
88039caa RS |
605 | we return a list having a nil as its car and the end of comment position |
606 | as cdr." | |
aaa114d0 | 607 | (save-excursion |
88039caa | 608 | ;; the idea is to look backwards for a comment start regexp, do a |
5a5fa834 | 609 | ;; forward comment, and see if we are inside, then extend |
88039caa RS |
610 | ;; forward and backward as long as we have comments |
611 | (let ((q (point))) | |
91d82a70 | 612 | (skip-chars-forward "[:blank:]") |
88039caa | 613 | (when (or (looking-at hs-c-start-regexp) |
26a0b399 | 614 | (re-search-backward hs-c-start-regexp (point-min) t)) |
aa7d6700 TTN |
615 | ;; first get to the beginning of this comment... |
616 | (while (and (not (bobp)) | |
617 | (= (point) (progn (forward-comment -1) (point)))) | |
618 | (forward-char -1)) | |
619 | ;; ...then extend backwards | |
26a0b399 TTN |
620 | (forward-comment (- (buffer-size))) |
621 | (skip-chars-forward " \t\n\f") | |
622 | (let ((p (point)) | |
aa7d6700 | 623 | (hidable t)) |
26a0b399 TTN |
624 | (beginning-of-line) |
625 | (unless (looking-at (concat "[ \t]*" hs-c-start-regexp)) | |
626 | ;; we are in this situation: (example) | |
627 | ;; (defun bar () | |
628 | ;; (foo) | |
629 | ;; ) ; comment | |
630 | ;; ^ | |
631 | ;; the point was here before doing (beginning-of-line) | |
632 | ;; here we should advance till the next comment which | |
633 | ;; eventually has only white spaces preceding it on the same | |
634 | ;; line | |
635 | (goto-char p) | |
636 | (forward-comment 1) | |
637 | (skip-chars-forward " \t\n\f") | |
638 | (setq p (point)) | |
639 | (while (and (< (point) q) | |
640 | (> (point) p) | |
641 | (not (looking-at hs-c-start-regexp))) | |
2bbf1842 TTN |
642 | ;; avoid an infinite cycle |
643 | (setq p (point)) | |
26a0b399 TTN |
644 | (forward-comment 1) |
645 | (skip-chars-forward " \t\n\f")) | |
c327c405 TTN |
646 | (when (or (not (looking-at hs-c-start-regexp)) |
647 | (> (point) q)) | |
648 | ;; we cannot hide this comment block | |
aa7d6700 | 649 | (setq hidable nil))) |
26a0b399 TTN |
650 | ;; goto the end of the comment |
651 | (forward-comment (buffer-size)) | |
652 | (skip-chars-backward " \t\n\f") | |
653 | (end-of-line) | |
c327c405 | 654 | (when (>= (point) q) |
aa7d6700 | 655 | (list (and hidable p) (point)))))))) |
6da7653c TTN |
656 | |
657 | (defun hs-grok-mode-type () | |
aaa114d0 TTN |
658 | "Set up hideshow variables for new buffers. |
659 | If `hs-special-modes-alist' has information associated with the | |
660 | current buffer's major mode, use that. | |
26a0b399 | 661 | Otherwise, guess start, end and `comment-start' regexps; `forward-sexp' |
aaa114d0 | 662 | function; and adjust-block-beginning function." |
b5190045 | 663 | (if (and (boundp 'comment-start) |
26a0b399 TTN |
664 | (boundp 'comment-end) |
665 | comment-start comment-end) | |
666 | (let* ((lookup (assoc major-mode hs-special-modes-alist)) | |
667 | (start-elem (or (nth 1 lookup) "\\s("))) | |
668 | (if (listp start-elem) | |
669 | ;; handle (START-REGEXP MDATA-SELECT) | |
670 | (setq hs-block-start-regexp (car start-elem) | |
671 | hs-block-start-mdata-select (cadr start-elem)) | |
672 | ;; backwards compatibility: handle simple START-REGEXP | |
673 | (setq hs-block-start-regexp start-elem | |
674 | hs-block-start-mdata-select 0)) | |
675 | (setq hs-block-end-regexp (or (nth 2 lookup) "\\s)") | |
676 | hs-c-start-regexp (or (nth 3 lookup) | |
677 | (let ((c-start-regexp | |
678 | (regexp-quote comment-start))) | |
679 | (if (string-match " +$" c-start-regexp) | |
680 | (substring c-start-regexp | |
681 | 0 (1- (match-end 0))) | |
682 | c-start-regexp))) | |
683 | hs-forward-sexp-func (or (nth 4 lookup) 'forward-sexp) | |
684 | hs-adjust-block-beginning (nth 5 lookup))) | |
c327c405 | 685 | (setq hs-minor-mode nil) |
48d33090 SM |
686 | (error "%s Mode doesn't support Hideshow Minor Mode" |
687 | (format-mode-line mode-name)))) | |
6da7653c TTN |
688 | |
689 | (defun hs-find-block-beginning () | |
aaa114d0 | 690 | "Reposition point at block-start. |
1a8e83dc | 691 | Return point, or nil if original point was not in a block." |
26a0b399 TTN |
692 | (let ((done nil) |
693 | (here (point))) | |
694 | ;; look if current line is block start | |
5012f24c | 695 | (if (hs-looking-at-block-start-p) |
26a0b399 TTN |
696 | (point) |
697 | ;; look backward for the start of a block that contains the cursor | |
698 | (while (and (re-search-backward hs-block-start-regexp nil t) | |
bfeef8b6 DK |
699 | ;; go again if in a comment or a string |
700 | (or (save-match-data (nth 8 (syntax-ppss))) | |
5012f24c DK |
701 | (not (setq done |
702 | (< here (save-excursion | |
703 | (hs-forward-sexp (match-data t) 1) | |
704 | (point)))))))) | |
26a0b399 TTN |
705 | (if done |
706 | (point) | |
707 | (goto-char here) | |
708 | nil)))) | |
6da7653c | 709 | |
aaa114d0 | 710 | (defun hs-hide-level-recursive (arg minp maxp) |
26a0b399 | 711 | "Recursively hide blocks ARG levels below point in region (MINP MAXP)." |
aaa114d0 TTN |
712 | (when (hs-find-block-beginning) |
713 | (setq minp (1+ (point))) | |
26a0b399 | 714 | (funcall hs-forward-sexp-func 1) |
aaa114d0 | 715 | (setq maxp (1- (point)))) |
a5b101dc TTN |
716 | (unless hs-allow-nesting |
717 | (hs-discard-overlays minp maxp)) | |
aaa114d0 TTN |
718 | (goto-char minp) |
719 | (while (progn | |
26a0b399 TTN |
720 | (forward-comment (buffer-size)) |
721 | (and (< (point) maxp) | |
722 | (re-search-forward hs-block-start-regexp maxp t))) | |
a87af185 | 723 | (when (save-match-data |
bfeef8b6 | 724 | (not (nth 8 (syntax-ppss)))) ; not inside comments or strings |
a87af185 DB |
725 | (if (> arg 1) |
726 | (hs-hide-level-recursive (1- arg) minp maxp) | |
727 | (goto-char (match-beginning hs-block-start-mdata-select)) | |
728 | (hs-hide-block-at-point t)))) | |
aaa114d0 TTN |
729 | (goto-char maxp)) |
730 | ||
6da7653c | 731 | (defmacro hs-life-goes-on (&rest body) |
e7f767c2 | 732 | "Evaluate BODY forms if variable `hs-minor-mode' is non-nil. |
26a0b399 TTN |
733 | In the dynamic context of this macro, `inhibit-point-motion-hooks' |
734 | and `case-fold-search' are both t." | |
735 | `(when hs-minor-mode | |
736 | (let ((inhibit-point-motion-hooks t) | |
737 | (case-fold-search t)) | |
12ab6a7d DN |
738 | ,@body))) |
739 | ||
5e336d3e | 740 | (put 'hs-life-goes-on 'edebug-form-spec '(&rest form)) |
6da7653c | 741 | |
a5b101dc TTN |
742 | (defun hs-overlay-at (position) |
743 | "Return hideshow overlay at POSITION, or nil if none to be found." | |
744 | (let ((overlays (overlays-at position)) | |
745 | ov found) | |
746 | (while (and (not found) (setq ov (car overlays))) | |
747 | (setq found (and (overlay-get ov 'hs) ov) | |
748 | overlays (cdr overlays))) | |
749 | found)) | |
750 | ||
9479d258 | 751 | (defun hs-already-hidden-p () |
88039caa | 752 | "Return non-nil if point is in an already-hidden block, otherwise nil." |
9479d258 | 753 | (save-excursion |
88039caa RS |
754 | (let ((c-reg (hs-inside-comment-p))) |
755 | (if (and c-reg (nth 0 c-reg)) | |
26a0b399 TTN |
756 | ;; point is inside a comment, and that comment is hidable |
757 | (goto-char (nth 0 c-reg)) | |
dce39ca8 | 758 | (end-of-line) |
c327c405 TTN |
759 | (when (and (not c-reg) |
760 | (hs-find-block-beginning) | |
5012f24c | 761 | (hs-looking-at-block-start-p)) |
c327c405 TTN |
762 | ;; point is inside a block |
763 | (goto-char (match-end 0))))) | |
9479d258 | 764 | (end-of-line) |
a5b101dc | 765 | (hs-overlay-at (point)))) |
9479d258 | 766 | |
1c292fc7 | 767 | ;; This function is not used anymore (Bug#700). |
26a0b399 TTN |
768 | (defun hs-c-like-adjust-block-beginning (initial) |
769 | "Adjust INITIAL, the buffer position after `hs-block-start-regexp'. | |
770 | Actually, point is never moved; a new position is returned that is | |
771 | the end of the C-function header. This adjustment function is meant | |
772 | to be assigned to `hs-adjust-block-beginning' for C-like modes." | |
88039caa | 773 | (save-excursion |
26a0b399 | 774 | (goto-char (1- initial)) |
88039caa RS |
775 | (forward-comment (- (buffer-size))) |
776 | (point))) | |
777 | ||
26a0b399 TTN |
778 | ;;--------------------------------------------------------------------------- |
779 | ;; commands | |
6da7653c | 780 | |
6da7653c | 781 | (defun hs-hide-all () |
26a0b399 TTN |
782 | "Hide all top level blocks, displaying only first and last lines. |
783 | Move point to the beginning of the line, and run the normal hook | |
aaa114d0 | 784 | `hs-hide-hook'. See documentation for `run-hooks'. |
26a0b399 | 785 | If `hs-hide-comments-when-hiding-all' is non-nil, also hide the comments." |
6da7653c TTN |
786 | (interactive) |
787 | (hs-life-goes-on | |
6da7653c | 788 | (save-excursion |
a5b101dc TTN |
789 | (unless hs-allow-nesting |
790 | (hs-discard-overlays (point-min) (point-max))) | |
6da7653c | 791 | (goto-char (point-min)) |
31a0385d TTN |
792 | (let ((spew (make-progress-reporter "Hiding all blocks..." |
793 | (point-min) (point-max))) | |
9b4a7800 TTN |
794 | (re (concat "\\(" |
795 | hs-block-start-regexp | |
796 | "\\)" | |
797 | (if hs-hide-comments-when-hiding-all | |
798 | (concat "\\|\\(" | |
799 | hs-c-start-regexp | |
800 | "\\)") | |
801 | "")))) | |
802 | (while (progn | |
803 | (unless hs-hide-comments-when-hiding-all | |
804 | (forward-comment (point-max))) | |
805 | (re-search-forward re (point-max) t)) | |
806 | (if (match-beginning 1) | |
43956923 | 807 | ;; We have found a block beginning. |
26a0b399 | 808 | (progn |
9b4a7800 | 809 | (goto-char (match-beginning 1)) |
43956923 SG |
810 | (unless (if hs-hide-all-non-comment-function |
811 | (funcall hs-hide-all-non-comment-function) | |
812 | (hs-hide-block-at-point t)) | |
813 | ;; Go to end of matched data to prevent from getting stuck | |
814 | ;; with an endless loop. | |
815 | (goto-char (match-end 0)))) | |
9b4a7800 | 816 | ;; found a comment, probably |
2bbf1842 | 817 | (let ((c-reg (hs-inside-comment-p))) |
9b4a7800 TTN |
818 | (when (and c-reg (car c-reg)) |
819 | (if (> (count-lines (car c-reg) (nth 1 c-reg)) 1) | |
820 | (hs-hide-block-at-point t c-reg) | |
821 | (goto-char (nth 1 c-reg)))))) | |
31a0385d TTN |
822 | (progress-reporter-update spew (point))) |
823 | (progress-reporter-done spew))) | |
6da7653c | 824 | (beginning-of-line) |
d877f247 | 825 | (run-hooks 'hs-hide-hook))) |
6da7653c TTN |
826 | |
827 | (defun hs-show-all () | |
26a0b399 | 828 | "Show everything then run `hs-show-hook'. See `run-hooks'." |
6da7653c TTN |
829 | (interactive) |
830 | (hs-life-goes-on | |
9479d258 | 831 | (message "Showing all blocks ...") |
a5b101dc TTN |
832 | (let ((hs-allow-nesting nil)) |
833 | (hs-discard-overlays (point-min) (point-max))) | |
9479d258 | 834 | (message "Showing all blocks ... done") |
d877f247 | 835 | (run-hooks 'hs-show-hook))) |
6da7653c | 836 | |
6da7653c | 837 | (defun hs-hide-block (&optional end) |
26a0b399 | 838 | "Select a block and hide it. With prefix arg, reposition at END. |
aaa114d0 | 839 | Upon completion, point is repositioned and the normal hook |
9479d258 | 840 | `hs-hide-hook' is run. See documentation for `run-hooks'." |
6da7653c TTN |
841 | (interactive "P") |
842 | (hs-life-goes-on | |
843 | (let ((c-reg (hs-inside-comment-p))) | |
88039caa | 844 | (cond |
aaa114d0 | 845 | ((and c-reg (or (null (nth 0 c-reg)) |
26a0b399 TTN |
846 | (<= (count-lines (car c-reg) (nth 1 c-reg)) 1))) |
847 | (message "(not enough comment lines to hide)")) | |
848 | ((or c-reg | |
5012f24c | 849 | (hs-looking-at-block-start-p) |
26a0b399 | 850 | (hs-find-block-beginning)) |
88039caa | 851 | (hs-hide-block-at-point end c-reg) |
88039caa | 852 | (run-hooks 'hs-hide-hook)))))) |
6da7653c TTN |
853 | |
854 | (defun hs-show-block (&optional end) | |
aaa114d0 | 855 | "Select a block and show it. |
26a0b399 | 856 | With prefix arg, reposition at END. Upon completion, point is |
aaa114d0 | 857 | repositioned and the normal hook `hs-show-hook' is run. |
26a0b399 | 858 | See documentation for functions `hs-hide-block' and `run-hooks'." |
6da7653c TTN |
859 | (interactive "P") |
860 | (hs-life-goes-on | |
26a0b399 TTN |
861 | (or |
862 | ;; first see if we have something at the end of the line | |
77a2bb53 | 863 | (let ((ov (hs-overlay-at (line-end-position))) |
a5b101dc TTN |
864 | (here (point))) |
865 | (when ov | |
866 | (goto-char | |
867 | (cond (end (overlay-end ov)) | |
868 | ((eq 'comment (overlay-get ov 'hs)) here) | |
869 | (t (+ (overlay-start ov) (overlay-get ov 'hs-b-offset))))) | |
870 | (delete-overlay ov) | |
871 | t)) | |
26a0b399 TTN |
872 | ;; not immediately obvious, look for a suitable block |
873 | (let ((c-reg (hs-inside-comment-p)) | |
874 | p q) | |
875 | (cond (c-reg | |
876 | (when (car c-reg) | |
877 | (setq p (car c-reg) | |
878 | q (cadr c-reg)))) | |
879 | ((and (hs-find-block-beginning) | |
2bbf1842 | 880 | ;; ugh, fresh match-data |
5012f24c | 881 | (hs-looking-at-block-start-p)) |
26a0b399 | 882 | (setq p (point) |
39696a77 | 883 | q (progn (hs-forward-sexp (match-data t) 1) (point))))) |
26a0b399 | 884 | (when (and p q) |
dfdc1af2 | 885 | (hs-discard-overlays p q) |
fb652bb5 JB |
886 | (goto-char (if end q (1+ p)))))) |
887 | (run-hooks 'hs-show-hook))) | |
6da7653c | 888 | |
aaa114d0 | 889 | (defun hs-hide-level (arg) |
26a0b399 TTN |
890 | "Hide all blocks ARG levels below this block. |
891 | The hook `hs-hide-hook' is run; see `run-hooks'." | |
aaa114d0 TTN |
892 | (interactive "p") |
893 | (hs-life-goes-on | |
894 | (save-excursion | |
895 | (message "Hiding blocks ...") | |
896 | (hs-hide-level-recursive arg (point-min) (point-max)) | |
897 | (message "Hiding blocks ... done")) | |
aaa114d0 TTN |
898 | (run-hooks 'hs-hide-hook))) |
899 | ||
a23c5037 TTN |
900 | (defun hs-toggle-hiding () |
901 | "Toggle hiding/showing of a block. | |
902 | See `hs-hide-block' and `hs-show-block'." | |
903 | (interactive) | |
904 | (hs-life-goes-on | |
905 | (if (hs-already-hidden-p) | |
906 | (hs-show-block) | |
907 | (hs-hide-block)))) | |
908 | ||
9479d258 | 909 | (defun hs-mouse-toggle-hiding (e) |
aaa114d0 | 910 | "Toggle hiding/showing of a block. |
26a0b399 TTN |
911 | This command should be bound to a mouse key. |
912 | Argument E is a mouse event used by `mouse-set-point'. | |
913 | See `hs-hide-block' and `hs-show-block'." | |
9479d258 | 914 | (interactive "@e") |
26a0b399 TTN |
915 | (hs-life-goes-on |
916 | (mouse-set-point e) | |
a23c5037 | 917 | (hs-toggle-hiding))) |
26a0b399 TTN |
918 | |
919 | (defun hs-hide-initial-comment-block () | |
920 | "Hide the first block of comments in a file. | |
921 | This can be useful if you have huge RCS logs in those comments." | |
922 | (interactive) | |
923 | (hs-life-goes-on | |
924 | (let ((c-reg (save-excursion | |
925 | (goto-char (point-min)) | |
926 | (skip-chars-forward " \t\n\f") | |
927 | (hs-inside-comment-p)))) | |
928 | (when c-reg | |
929 | (let ((beg (car c-reg)) (end (cadr c-reg))) | |
930 | ;; see if we have enough comment lines to hide | |
931 | (when (> (count-lines beg end) 1) | |
932 | (hs-hide-comment-region beg end))))))) | |
9479d258 | 933 | |
6da7653c | 934 | ;;;###autoload |
1f344760 | 935 | (define-minor-mode hs-minor-mode |
fb652bb5 | 936 | "Minor mode to selectively hide/show code and comment blocks. |
e1ac4066 GM |
937 | With a prefix argument ARG, enable the mode if ARG is positive, |
938 | and disable it otherwise. If called from Lisp, enable the mode | |
939 | if ARG is omitted or nil. | |
940 | ||
c1ff6dac | 941 | When hideshow minor mode is on, the menu bar is augmented with hideshow |
aaa114d0 | 942 | commands and the hideshow commands are enabled. |
88039caa | 943 | The value '(hs . t) is added to `buffer-invisibility-spec'. |
d877f247 | 944 | |
aaa114d0 | 945 | The main commands are: `hs-hide-all', `hs-show-all', `hs-hide-block', |
9b4a7800 | 946 | `hs-show-block', `hs-hide-level' and `hs-toggle-hiding'. There is also |
26a0b399 | 947 | `hs-hide-initial-comment-block' and `hs-mouse-toggle-hiding'. |
88039caa | 948 | |
c1ff6dac | 949 | Turning hideshow minor mode off reverts the menu bar and the |
88039caa RS |
950 | variables to default values and disables the hideshow commands. |
951 | ||
a23c5037 TTN |
952 | Lastly, the normal hook `hs-minor-mode-hook' is run using `run-hooks'. |
953 | ||
88039caa RS |
954 | Key bindings: |
955 | \\{hs-minor-mode-map}" | |
fb652bb5 | 956 | :group 'hideshow |
1f344760 DN |
957 | :lighter " hs" |
958 | :keymap hs-minor-mode-map | |
959 | (setq hs-headline nil) | |
6da7653c TTN |
960 | (if hs-minor-mode |
961 | (progn | |
a23c5037 | 962 | (hs-grok-mode-type) |
b9634f95 | 963 | ;; Turn off this mode if we change major modes. |
dce39ca8 TTN |
964 | (add-hook 'change-major-mode-hook |
965 | 'turn-off-hideshow | |
966 | nil t) | |
26a0b399 | 967 | (easy-menu-add hs-minor-mode-menu) |
3f3960a5 | 968 | (set (make-local-variable 'line-move-ignore-invisible) t) |
a23c5037 | 969 | (add-to-invisibility-spec '(hs . t))) |
b9634f95 GM |
970 | (remove-from-invisibility-spec '(hs . t)) |
971 | ;; hs-show-all does nothing unless h-m-m is non-nil. | |
972 | (let ((hs-minor-mode t)) | |
1f344760 | 973 | (hs-show-all)))) |
6da7653c | 974 | |
996ae468 CY |
975 | ;;;###autoload |
976 | (defun turn-off-hideshow () | |
1fee5894 | 977 | "Unconditionally turn off `hs-minor-mode'." |
996ae468 CY |
978 | (hs-minor-mode -1)) |
979 | ||
26a0b399 TTN |
980 | ;;--------------------------------------------------------------------------- |
981 | ;; that's it | |
6da7653c TTN |
982 | |
983 | (provide 'hideshow) | |
984 | ||
985 | ;;; hideshow.el ends here |