(local-write-file-hooks): Make this variable buffer-local.
[bpt/emacs.git] / lisp / textmodes / ooutline.el
CommitLineData
6594deb0
ER
1;;; outline.el --- outline mode commands for Emacs
2
476731da 3;; Copyright (C) 1986, 1993 Free Software Foundation, Inc.
26d1e4fd 4
9750e079
ER
5;; Maintainer: FSF
6
26d1e4fd
BP
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
e5167999 11;; the Free Software Foundation; either version 2, or (at your option)
26d1e4fd
BP
12;; any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs; see the file COPYING. If not, write to
21;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22
edbd2f74
ER
23;;; Commentary:
24
25;; This package is a major mode for editing outline-format documents.
26;; An outline can be `abstracted' to show headers at any given level,
27;; with all stuff below hidden. See the Emacs manual for details.
28
e5167999
ER
29;;; Code:
30
26d1e4fd
BP
31;; Jan '86, Some new features added by Peter Desnoyers and rewritten by RMS.
32
33(defvar outline-regexp "[*\^l]+"
34 "*Regular expression to match the beginning of a heading.
35Any line whose beginning matches this regexp is considered to start a heading.
36The recommended way to set this is with a Local Variables: list
37in the file it applies to. See also outline-heading-end-regexp.")
38
8f1e8ff0 39(defvar outline-heading-end-regexp "[\n\^M]"
26d1e4fd
BP
40 "*Regular expression to match the end of a heading line.
41You can assume that point is at the beginning of a heading when this
42regexp is searched for. The heading ends at the end of the match.
43The recommended way to set this is with a \"Local Variables:\" list
44in the file it applies to.")
45
46(defvar outline-mode-map nil "")
47
48(if outline-mode-map
49 nil
50 (setq outline-mode-map (nconc (make-sparse-keymap) text-mode-map))
51 (define-key outline-mode-map "\C-c\C-n" 'outline-next-visible-heading)
52 (define-key outline-mode-map "\C-c\C-p" 'outline-previous-visible-heading)
53 (define-key outline-mode-map "\C-c\C-i" 'show-children)
54 (define-key outline-mode-map "\C-c\C-s" 'show-subtree)
55 (define-key outline-mode-map "\C-c\C-h" 'hide-subtree)
56 (define-key outline-mode-map "\C-c\C-u" 'outline-up-heading)
57 (define-key outline-mode-map "\C-c\C-f" 'outline-forward-same-level)
ee1a4f84
RS
58 (define-key outline-mode-map "\C-c\C-b" 'outline-backward-same-level)
59
60 (define-key outline-mode-map [menu-bar hide]
61 (cons "Hide" (make-sparse-keymap "Hide")))
62
63 (define-key outline-mode-map [menu-bar hide hide-subtree]
32e61280 64 '("Hide Subtree" . hide-subtree))
ee1a4f84 65 (define-key outline-mode-map [menu-bar hide hide-entry]
32e61280 66 '("Hide Entry" . hide-entry))
ee1a4f84 67 (define-key outline-mode-map [menu-bar hide hide-body]
32e61280 68 '("Hide Body" . hide-body))
ee1a4f84 69 (define-key outline-mode-map [menu-bar hide hide-leaves]
32e61280 70 '("Hide Leaves" . hide-leaves))
ee1a4f84
RS
71
72 (define-key outline-mode-map [menu-bar show]
73 (cons "Show" (make-sparse-keymap "Show")))
74
75 (define-key outline-mode-map [menu-bar show show-subtree]
32e61280 76 '("Show Subtree" . show-subtree))
ee1a4f84 77 (define-key outline-mode-map [menu-bar show show-children]
32e61280 78 '("Show Children" . show-children))
ee1a4f84 79 (define-key outline-mode-map [menu-bar show show-branches]
32e61280 80 '("Show Branches" . show-branches))
ee1a4f84 81 (define-key outline-mode-map [menu-bar show show-entry]
32e61280 82 '("Show Entry" . show-entry))
ee1a4f84 83 (define-key outline-mode-map [menu-bar show show-all]
32e61280 84 '("Show All" . show-all))
ee1a4f84
RS
85
86 (define-key outline-mode-map [menu-bar headings]
87 (cons "Headings" (make-sparse-keymap "Headings")))
88
89 (define-key outline-mode-map [menu-bar headings outline-backward-same-level]
90 '("Previous Same Level" . outline-backward-same-level))
91 (define-key outline-mode-map [menu-bar headings outline-forward-same-level]
92 '("Next Same Level" . outline-forward-same-level))
93 (define-key outline-mode-map [menu-bar headings outline-previous-visible-heading]
94 '("Previous" . outline-previous-visible-heading))
95 (define-key outline-mode-map [menu-bar headings outline-next-visible-heading]
96 '("Next" . outline-next-visible-heading))
97 (define-key outline-mode-map [menu-bar headings outline-up-heading]
98 '("Up" . outline-up-heading)))
26d1e4fd
BP
99
100(defvar outline-minor-mode nil
101 "Non-nil if using Outline mode as a minor mode of some other mode.")
326c43dc
RS
102(make-variable-buffer-local 'outline-minor-mode)
103(put 'outline-minor-mode 'permanent-local t)
26d1e4fd
BP
104(setq minor-mode-alist (append minor-mode-alist
105 (list '(outline-minor-mode " Outl"))))
106
f9f9507e 107;;;###autoload
26d1e4fd
BP
108(defun outline-mode ()
109 "Set major mode for editing outlines with selective display.
110Headings are lines which start with asterisks: one for major headings,
111two for subheadings, etc. Lines not starting with asterisks are body lines.
112
113Body text or subheadings under a heading can be made temporarily
114invisible, or visible again. Invisible lines are attached to the end
115of the heading, so they move with it, if the line is killed and yanked
116back. A heading with text hidden under it is marked with an ellipsis (...).
117
118Commands:\\<outline-mode-map>
119\\[outline-next-visible-heading] outline-next-visible-heading move by visible headings
120\\[outline-previous-visible-heading] outline-previous-visible-heading
121\\[outline-forward-same-level] outline-forward-same-level similar but skip subheadings
122\\[outline-backward-same-level] outline-backward-same-level
123\\[outline-up-heading] outline-up-heading move from subheading to heading
124
125M-x hide-body make all text invisible (not headings).
126M-x show-all make everything in buffer visible.
127
128The remaining commands are used when point is on a heading line.
129They apply to some of the body or subheadings of that heading.
130\\[hide-subtree] hide-subtree make body and subheadings invisible.
131\\[show-subtree] show-subtree make body and subheadings visible.
132\\[show-children] show-children make direct subheadings visible.
133 No effect on body, or subheadings 2 or more levels down.
134 With arg N, affects subheadings N levels down.
135M-x hide-entry make immediately following body invisible.
136M-x show-entry make it visible.
137M-x hide-leaves make body under heading and under its subheadings invisible.
138 The subheadings remain visible.
139M-x show-branches make all subheadings at all levels visible.
140
141The variable `outline-regexp' can be changed to control what is a heading.
142A line is a heading if `outline-regexp' matches something at the
143beginning of the line. The longer the match, the deeper the level.
144
145Turning on outline mode calls the value of `text-mode-hook' and then of
146`outline-mode-hook', if they are non-nil."
147 (interactive)
148 (kill-all-local-variables)
149 (setq selective-display t)
150 (use-local-map outline-mode-map)
151 (setq mode-name "Outline")
152 (setq major-mode 'outline-mode)
153 (define-abbrev-table 'text-mode-abbrev-table ())
154 (setq local-abbrev-table text-mode-abbrev-table)
155 (set-syntax-table text-mode-syntax-table)
156 (make-local-variable 'paragraph-start)
157 (setq paragraph-start (concat paragraph-start "\\|^\\("
158 outline-regexp "\\)"))
159 ;; Inhibit auto-filling of header lines.
160 (make-local-variable 'auto-fill-inhibit-regexp)
161 (setq auto-fill-inhibit-regexp outline-regexp)
162 (make-local-variable 'paragraph-separate)
163 (setq paragraph-separate (concat paragraph-separate "\\|^\\("
164 outline-regexp "\\)"))
165 (run-hooks 'text-mode-hook 'outline-mode-hook))
166
6e301d24
RS
167(defvar outline-minor-mode-prefix "\C-c"
168 "*Prefix key to use for Outline commands in Outline minor mode.")
169
8f1e8ff0
RS
170(defvar outline-minor-mode-map nil)
171(if outline-minor-mode-map
172 nil
173 (setq outline-minor-mode-map (make-sparse-keymap))
ee1a4f84
RS
174 (define-key outline-minor-mode-map [menu-bar]
175 (lookup-key outline-mode-map [menu-bar]))
6e301d24 176 (define-key outline-minor-mode-map outline-minor-mode-prefix
8f1e8ff0
RS
177 (lookup-key outline-mode-map "\C-c")))
178
179(or (assq 'outline-minor-mode minor-mode-map-alist)
180 (setq minor-mode-map-alist
181 (cons (cons 'outline-minor-mode outline-minor-mode-map)
182 minor-mode-map-alist)))
183
34060080 184;;;###autoload
8f1e8ff0 185(defun outline-minor-mode (&optional arg)
326c43dc
RS
186 "Toggle Outline minor mode.
187With arg, turn Outline minor mode on if arg is positive, off otherwise.
188See the command `outline-mode' for more information on this mode."
26d1e4fd
BP
189 (interactive "P")
190 (setq outline-minor-mode
191 (if (null arg) (not outline-minor-mode)
192 (> (prefix-numeric-value arg) 0)))
193 (if outline-minor-mode
194 (progn
195 (setq selective-display t)
26d1e4fd 196 (run-hooks 'outline-minor-mode-hook))
8f1e8ff0 197 (setq selective-display nil)))
26d1e4fd 198\f
476731da
RS
199(defvar outline-level 'outline-level
200 "Function of no args to compute a header's nesting level in an outline.
201It can assume point is at the beginning of a header line.")
202
26d1e4fd
BP
203(defun outline-level ()
204 "Return the depth to which a statement is nested in the outline.
205Point must be at the beginning of a header line. This is actually
206the column number of the end of what `outline-regexp matches'."
207 (save-excursion
208 (looking-at outline-regexp)
209 (save-excursion (goto-char (match-end 0)) (current-column))))
210
211(defun outline-next-preface ()
212 "Skip forward to just before the next heading line."
213 (if (re-search-forward (concat "[\n\^M]\\(" outline-regexp "\\)")
214 nil 'move)
215 (goto-char (match-beginning 0)))
216 (if (memq (preceding-char) '(?\n ?\^M))
217 (forward-char -1)))
218
219(defun outline-next-heading ()
220 "Move to the next (possibly invisible) heading line."
221 (interactive)
222 (if (re-search-forward (concat "[\n\^M]\\(" outline-regexp "\\)")
223 nil 'move)
224 (goto-char (1+ (match-beginning 0)))))
225
226(defun outline-back-to-heading ()
227 "Move to previous (possibly invisible) heading line,
228or to the beginning of this line if it is a heading line."
229 (beginning-of-line)
230 (or (outline-on-heading-p)
231 (re-search-backward (concat "^\\(" outline-regexp "\\)") nil 'move)))
232
233(defun outline-on-heading-p ()
234 "Return T if point is on a header line."
235 (save-excursion
236 (beginning-of-line)
237 (and (eq (preceding-char) ?\n)
238 (looking-at outline-regexp))))
239
240(defun outline-end-of-heading ()
241 (if (re-search-forward outline-heading-end-regexp nil 'move)
242 (forward-char -1)))
243
244(defun outline-next-visible-heading (arg)
245 "Move to the next visible heading line.
246With argument, repeats or can move backward if negative.
247A heading line is one that starts with a `*' (or that
248`outline-regexp' matches)."
249 (interactive "p")
250 (if (< arg 0)
251 (beginning-of-line)
252 (end-of-line))
253 (re-search-forward (concat "^\\(" outline-regexp "\\)") nil nil arg)
254 (beginning-of-line))
255
256(defun outline-previous-visible-heading (arg)
257 "Move to the previous heading line.
258With argument, repeats or can move forward if negative.
259A heading line is one that starts with a `*' (or that
260`outline-regexp' matches)."
261 (interactive "p")
262 (outline-next-visible-heading (- arg)))
263
264(defun outline-flag-region (from to flag)
265 "Hides or shows lines from FROM to TO, according to FLAG.
266If FLAG is `\\n' (newline character) then text is shown,
267while if FLAG is `\\^M' (control-M) the text is hidden."
05cf4426
RS
268 (let (buffer-read-only)
269 (subst-char-in-region from to
270 (if (= flag ?\n) ?\^M ?\n)
271 flag t)))
26d1e4fd
BP
272\f
273(defun hide-entry ()
274 "Hide the body directly following this heading."
275 (interactive)
276 (outline-back-to-heading)
277 (outline-end-of-heading)
278 (save-excursion
279 (outline-flag-region (point) (progn (outline-next-preface) (point)) ?\^M)))
280
281(defun show-entry ()
282 "Show the body directly following this heading."
283 (interactive)
284 (save-excursion
285 (outline-flag-region (point) (progn (outline-next-preface) (point)) ?\n)))
286
287(defun hide-body ()
288 "Hide all of buffer except headings."
289 (interactive)
290 (hide-region-body (point-min) (point-max)))
291
292(defun hide-region-body (start end)
293 "Hide all body lines in the region, but not headings."
294 (save-excursion
295 (save-restriction
296 (narrow-to-region start end)
297 (goto-char (point-min))
298 (if (outline-on-heading-p)
299 (outline-end-of-heading))
300 (while (not (eobp))
301 (outline-flag-region (point)
302 (progn (outline-next-preface) (point)) ?\^M)
303 (if (not (eobp))
304 (progn
305 (forward-char
306 (if (looking-at "[\n\^M][\n\^M]")
307 2 1))
308 (outline-end-of-heading)))))))
309
310(defun show-all ()
311 "Show all of the text in the buffer."
312 (interactive)
313 (outline-flag-region (point-min) (point-max) ?\n))
314
315(defun hide-subtree ()
316 "Hide everything after this heading at deeper levels."
317 (interactive)
318 (outline-flag-subtree ?\^M))
319
320(defun hide-leaves ()
321 "Hide all body after this heading at deeper levels."
322 (interactive)
323 (outline-back-to-heading)
324 (outline-end-of-heading)
325 (hide-region-body (point) (progn (outline-end-of-subtree) (point))))
326
327(defun show-subtree ()
328 "Show everything after this heading at deeper levels."
329 (interactive)
330 (outline-flag-subtree ?\n))
331
332(defun outline-flag-subtree (flag)
333 (save-excursion
334 (outline-back-to-heading)
335 (outline-end-of-heading)
336 (outline-flag-region (point)
337 (progn (outline-end-of-subtree) (point))
338 flag)))
339
340(defun outline-end-of-subtree ()
341 (outline-back-to-heading)
342 (let ((opoint (point))
343 (first t)
476731da 344 (level (funcall outline-level)))
26d1e4fd 345 (while (and (not (eobp))
476731da 346 (or first (> (funcall outline-level) level)))
26d1e4fd
BP
347 (setq first nil)
348 (outline-next-heading))
349 (forward-char -1)
350 (if (memq (preceding-char) '(?\n ?\^M))
351 (forward-char -1))))
352\f
353(defun show-branches ()
354 "Show all subheadings of this heading, but not their bodies."
355 (interactive)
356 (show-children 1000))
357
358(defun show-children (&optional level)
359 "Show all direct subheadings of this heading.
360Prefix arg LEVEL is how many levels below the current level should be shown.
361Default is enough to cause the following heading to appear."
362 (interactive "P")
363 (setq level
364 (if level (prefix-numeric-value level)
365 (save-excursion
366 (beginning-of-line)
476731da 367 (let ((start-level (funcall outline-level)))
26d1e4fd 368 (outline-next-heading)
476731da 369 (max 1 (- (funcall outline-level) start-level))))))
26d1e4fd
BP
370 (save-excursion
371 (save-restriction
372 (beginning-of-line)
476731da 373 (setq level (+ level (funcall outline-level)))
26d1e4fd
BP
374 (narrow-to-region (point)
375 (progn (outline-end-of-subtree) (1+ (point))))
376 (goto-char (point-min))
377 (while (and (not (eobp))
378 (progn
379 (outline-next-heading)
380 (not (eobp))))
476731da 381 (if (<= (funcall outline-level) level)
26d1e4fd
BP
382 (save-excursion
383 (outline-flag-region (save-excursion
384 (forward-char -1)
385 (if (memq (preceding-char) '(?\n ?\^M))
386 (forward-char -1))
387 (point))
388 (progn (outline-end-of-heading) (point))
389 ?\n)))))))
390\f
391(defun outline-up-heading (arg)
392 "Move to the heading line of which the present line is a subheading.
393With argument, move up ARG levels."
394 (interactive "p")
395 (outline-back-to-heading)
476731da 396 (if (eq (funcall outline-level) 1)
26d1e4fd 397 (error ""))
476731da 398 (while (and (> (funcall outline-level) 1)
26d1e4fd
BP
399 (> arg 0)
400 (not (bobp)))
476731da
RS
401 (let ((present-level (funcall outline-level)))
402 (while (not (< (funcall outline-level) present-level))
26d1e4fd
BP
403 (outline-previous-visible-heading 1))
404 (setq arg (- arg 1)))))
405
406(defun outline-forward-same-level (arg)
407 "Move forward to the ARG'th subheading from here of the same level as the
408present one. It stops at the first and last subheadings of a superior heading."
409 (interactive "p")
410 (outline-back-to-heading)
411 (while (> arg 0)
412 (let ((point-to-move-to (save-excursion
413 (outline-get-next-sibling))))
414 (if point-to-move-to
415 (progn
416 (goto-char point-to-move-to)
417 (setq arg (1- arg)))
418 (progn
419 (setq arg 0)
420 (error ""))))))
421
422(defun outline-get-next-sibling ()
423 "Position the point at the next heading of the same level,
424and return that position or nil if it cannot be found."
476731da 425 (let ((level (funcall outline-level)))
26d1e4fd 426 (outline-next-visible-heading 1)
476731da 427 (while (and (> (funcall outline-level) level)
26d1e4fd
BP
428 (not (eobp)))
429 (outline-next-visible-heading 1))
476731da 430 (if (< (funcall outline-level) level)
26d1e4fd
BP
431 nil
432 (point))))
433
434(defun outline-backward-same-level (arg)
435 "Move backward to the ARG'th subheading from here of the same level as the
436present one. It stops at the first and last subheadings of a superior heading."
437 (interactive "p")
438 (outline-back-to-heading)
439 (while (> arg 0)
440 (let ((point-to-move-to (save-excursion
441 (outline-get-last-sibling))))
442 (if point-to-move-to
443 (progn
444 (goto-char point-to-move-to)
445 (setq arg (1- arg)))
446 (progn
447 (setq arg 0)
448 (error ""))))))
449
450(defun outline-get-last-sibling ()
451 "Position the point at the previous heading of the same level,
452and return that position or nil if it cannot be found."
476731da 453 (let ((level (funcall outline-level)))
26d1e4fd 454 (outline-previous-visible-heading 1)
476731da 455 (while (and (> (funcall outline-level) level)
26d1e4fd
BP
456 (not (bobp)))
457 (outline-previous-visible-heading 1))
476731da 458 (if (< (funcall outline-level) level)
26d1e4fd
BP
459 nil
460 (point))))
461
8f1e8ff0
RS
462(provide 'outline)
463
6594deb0 464;;; outline.el ends here