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