Clarify that bootstrap and the shorter procedure are alternatives.
[bpt/emacs.git] / lisp / allout.el
CommitLineData
6a05d05f 1;;; allout.el --- extensive outline mode for use alone and with other modes
19b84ba3 2
0d30b337 3;; Copyright (C) 1992, 1993, 1994, 2001, 2002, 2003, 2004,
31468dcb 4;; 2005, 2006 Free Software Foundation, Inc.
1977b8f6 5
d82979ea
EZ
6;; Author: Ken Manheimer <ken dot manheimer at gmail dot com>
7;; Maintainer: Ken Manheimer <ken dot manheimer at gmail dot com>
c567ac01 8;; Created: Dec 1991 - first release to usenet
4034b0e2 9;; Version: 2.2.1
d82979ea 10;; Keywords: outlines wp languages
01fc9422 11;; Website: http://myriadicity.net/Sundry/EmacsAllout
1977b8f6
RS
12
13;; This file is part of GNU Emacs.
14
59243403
RS
15;; GNU Emacs is free software; you can redistribute it and/or modify
16;; it under the terms of the GNU General Public License as published by
17;; the Free Software Foundation; either version 2, or (at your option)
18;; any later version.
19
1977b8f6 20;; GNU Emacs is distributed in the hope that it will be useful,
59243403
RS
21;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23;; GNU General Public License for more details.
24
25;; You should have received a copy of the GNU General Public License
b578f267 26;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
27;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28;; Boston, MA 02110-1301, USA.
1977b8f6 29
6a05d05f 30;;; Commentary:
19b84ba3 31
96b83743
EZ
32;; Allout outline minor mode provides extensive outline formatting and
33;; and manipulation beyond standard emacs outline mode. Some features:
71296446 34;;
96b83743
EZ
35;; - Classic outline-mode topic-oriented navigation and exposure adjustment
36;; - Topic-oriented editing including coherent topic and subtopic
37;; creation, promotion, demotion, cut/paste across depths, etc.
38;; - Incremental search with dynamic exposure and reconcealment of text
39;; - Customizable bullet format - enables programming-language specific
40;; outlining, for code-folding editing. (Allout code itself is to try it;
ac052b48 41;; formatted as an outline - do ESC-x eval-buffer in allout.el; but
96b83743
EZ
42;; emacs local file variables need to be enabled when the
43;; file was visited - see `enable-local-variables'.)
44;; - Configurable per-file initial exposure settings
45;; - Symmetric-key and key-pair topic encryption, plus symmetric passphrase
0949617b
EZ
46;; mnemonic support, with verification against an established passphrase
47;; (using a stashed encrypted dummy string) and user-supplied hint
96b83743
EZ
48;; maintenance. (See allout-toggle-current-subtree-encryption docstring.)
49;; - Automatic topic-number maintenance
50;; - "Hot-spot" operation, for single-keystroke maneuvering and
d82979ea 51;; exposure control (see the allout-mode docstring)
96b83743 52;; - Easy rendering of exposed portions into numbered, latex, indented, etc
d82979ea 53;; outline styles
96b83743
EZ
54;; - Careful attention to whitespace - enabling blank lines between items
55;; and maintenance of hanging indentation (in paragraph auto-fill and
56;; across topic promotion and demotion) of topic bodies consistent with
57;; indentation of their topic header.
71296446 58;;
d82979ea 59;; and more.
71296446 60;;
96b83743 61;; See the `allout-mode' function's docstring for an introduction to the
01fc9422
EZ
62;; mode.
63;;
64;; The latest development version and helpful notes are available at
96b83743
EZ
65;; http://myriadicity.net/Sundry/EmacsAllout .
66;;
9179616f 67;; The outline menubar additions provide quick reference to many of
d82979ea
EZ
68;; the features, and see the docstring of the variable `allout-init'
69;; for instructions on priming your emacs session for automatic
70;; activation of allout-mode.
71296446 71;;
fd5359c6
MR
72;; See the docstring of the variables `allout-layout' and
73;; `allout-auto-activation' for details on automatic activation of
d82979ea 74;; `allout-mode' as a minor mode. (It has changed since allout
19b84ba3 75;; 3.x, for those of you that depend on the old method.)
c567ac01 76;;
353e2ef2 77;; Note - the lines beginning with `;;;_' are outline topic headers.
ac052b48 78;; Just `ESC-x eval-buffer' to give it a whirl.
19b84ba3 79
d82979ea 80;; ken manheimer (ken dot manheimer at gmail dot com)
c567ac01 81
6a05d05f
PJ
82;;; Code:
83
d82979ea 84;;;_* Dependency autoloads
96b83743 85(require 'overlay)
01fc9422
EZ
86(eval-when-compile
87 ;; Most of the requires here are for stuff covered by autoloads.
88 ;; Since just byte-compiling doesn't trigger autoloads, so that
89 ;; "function not found" warnings would occur without these requires.
90 (progn
91 (require 'pgg)
92 (require 'pgg-gpg)
93 (require 'overlay)
94 ;; `cl' is required for `assert'. `assert' is not covered by a standard
95 ;; autoload, but it is a macro, so that eval-when-compile is sufficient
96 ;; to byte-compile it in, or to do the require when the buffer evalled.
97 (require 'cl)
98 ))
d82979ea 99
19b84ba3 100;;;_* USER CUSTOMIZATION VARIABLES:
96b83743
EZ
101
102;;;_ > defgroup allout
4bef9110
SE
103(defgroup allout nil
104 "Extensive outline mode for use alone and with other modes."
fd5359c6 105 :prefix "allout-"
d82979ea 106 :group 'outlines)
19b84ba3
RS
107
108;;;_ + Layout, Mode, and Topic Header Configuration
109
fd5359c6
MR
110;;;_ = allout-auto-activation
111(defcustom allout-auto-activation nil
112 "*Regulates auto-activation modality of allout outlines - see `allout-init'.
19b84ba3 113
fd5359c6 114Setq-default by `allout-init' to regulate whether or not allout
19b84ba3 115outline mode is automatically activated when the buffer-specific
fd5359c6
MR
116variable `allout-layout' is non-nil, and whether or not the layout
117dictated by `allout-layout' should be imposed on mode activation.
19b84ba3 118
24190da5 119With value t, auto-mode-activation and auto-layout are enabled.
aad94676 120\(This also depends on `allout-find-file-hook' being installed in
95c12694 121`find-file-hook', which is also done by `allout-init'.)
19b84ba3
RS
122
123With value `ask', auto-mode-activation is enabled, and endorsement for
124performing auto-layout is asked of the user each time.
125
71296446 126With value `activate', only auto-mode-activation is enabled,
353e2ef2 127auto-layout is not.
19b84ba3 128
24190da5 129With value nil, neither auto-mode-activation nor auto-layout are
19b84ba3
RS
130enabled.
131
fd5359c6 132See the docstring for `allout-init' for the proper interface to
9179616f
DL
133this variable."
134 :type '(choice (const :tag "On" t)
135 (const :tag "Ask about layout" "ask")
136 (const :tag "Mode only" "activate")
137 (const :tag "Off" nil))
138 :group 'allout)
4034b0e2
LT
139;;;_ = allout-default-layout
140(defcustom allout-default-layout '(-2 : 0)
141 "*Default allout outline layout specification.
142
143This setting specifies the outline exposure to use when
144`allout-layout' has the local value `t'. This docstring describes the
145layout specifications.
146
147A list value specifies a default layout for the current buffer,
148to be applied upon activation of `allout-mode'. Any non-nil
149value will automatically trigger `allout-mode', provided
150`allout-init' has been called to enable this behavior.
151
152The types of elements in the layout specification are:
153
154 integer - dictate the relative depth to open the corresponding topic(s),
155 where:
156 - negative numbers force the topic to be closed before opening
157 to the absolute value of the number, so all siblings are open
158 only to that level.
159 - positive numbers open to the relative depth indicated by the
160 number, but do not force already opened subtopics to be closed.
161 - 0 means to close topic - hide all subitems.
162 : - repeat spec - apply the preceeding element to all siblings at
163 current level, *up to* those siblings that would be covered by specs
164 following the `:' on the list. Ie, apply to all topics at level but
165 trailing ones accounted for by trailing specs. \(Only the first of
166 multiple colons at the same level is honored - later ones are ignored.)
167 * - completely exposes the topic, including bodies
168 + - exposes all subtopics, but not the bodies
169 - - exposes the body of the corresponding topic, but not subtopics
170 list - a nested layout spec, to be applied intricately to its
171 corresponding item(s)
1977b8f6 172
4034b0e2
LT
173Examples:
174 '(-2 : 0)
175 Collapse the top-level topics to show their children and
176 grandchildren, but completely collapse the final top-level topic.
177 '(-1 () : 1 0)
178 Close the first topic so only the immediate subtopics are shown,
179 leave the subsequent topics exposed as they are until the second
180 second to last topic, which is exposed at least one level, and
181 completely close the last topic.
182 '(-2 : -1 *)
183 Expose children and grandchildren of all topics at current
184 level except the last two; expose children of the second to
185 last and completely expose the last one, including its subtopics.
19b84ba3 186
4034b0e2 187See `allout-expose-topic' for more about the exposure process.
19b84ba3 188
353e2ef2
KH
189Also, allout's mode-specific provisions will make topic prefixes default
190to the comment-start string, if any, of the language of the file. This
4034b0e2
LT
191is modulo the setting of `allout-use-mode-specific-leader', which see."
192 :type 'allout-layout-type
193 :group 'allout)
194;;;_ : allout-layout-type
195(define-widget 'allout-layout-type 'lazy
196 "Allout layout format customization basic building blocks."
197 :type '(repeat
198 (choice (integer :tag "integer (<= zero is strict)")
199 (const :tag ": (repeat prior)" :)
200 (const :tag "* (completely expose)" *)
201 (const :tag "+ (expose all offspring, headlines only)" +)
202 (const :tag "- (expose topic body but not offspring)" -)
203 (allout-layout-type :tag "<Nested layout>"))))
204
fd5359c6
MR
205;;;_ = allout-show-bodies
206(defcustom allout-show-bodies nil
9179616f
DL
207 "*If non-nil, show entire body when exposing a topic, rather than
208just the header."
209 :type 'boolean
210 :group 'allout)
fd5359c6 211(make-variable-buffer-local 'allout-show-bodies)
8570ce80
SM
212;;;###autoload
213(put 'allout-show-bodies 'safe-local-variable
64686e6d 214 (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
1977b8f6 215
fd5359c6
MR
216;;;_ = allout-header-prefix
217(defcustom allout-header-prefix "."
19b84ba3
RS
218 "*Leading string which helps distinguish topic headers.
219
220Outline topic header lines are identified by a leading topic
c567ac01
RS
221header prefix, which mostly have the value of this var at their front.
222\(Level 1 topics are exceptions. They consist of only a single
2265e017 223character, which is typically set to the `allout-primary-bullet'. Many
4bef9110
SE
224outlines start at level 2 to avoid this discrepancy."
225 :type 'string
226 :group 'allout)
fd5359c6 227(make-variable-buffer-local 'allout-header-prefix)
8570ce80
SM
228;;;###autoload
229(put 'allout-header-prefix 'safe-local-variable 'stringp)
fd5359c6
MR
230;;;_ = allout-primary-bullet
231(defcustom allout-primary-bullet "*"
19b84ba3
RS
232 "Bullet used for top-level outline topics.
233
234Outline topic header lines are identified by a leading topic header
c567ac01 235prefix, which is concluded by bullets that includes the value of this
fd5359c6 236var and the respective allout-*-bullets-string vars.
c567ac01 237
353e2ef2 238The value of an asterisk (`*') provides for backwards compatibility
e126900f 239with the original Emacs outline mode. See `allout-plain-bullets-string'
2265e017 240and `allout-distinctive-bullets-string' for the range of available
4bef9110
SE
241bullets."
242 :type 'string
243 :group 'allout)
fd5359c6 244(make-variable-buffer-local 'allout-primary-bullet)
8570ce80
SM
245;;;###autoload
246(put 'allout-primary-bullet 'safe-local-variable 'stringp)
fd5359c6 247;;;_ = allout-plain-bullets-string
d82979ea 248(defcustom allout-plain-bullets-string ".,"
19b84ba3
RS
249 "*The bullets normally used in outline topic prefixes.
250
fd5359c6 251See `allout-distinctive-bullets-string' for the other kind of
c567ac01 252bullets.
1977b8f6 253
353e2ef2 254DO NOT include the close-square-bracket, `]', as a bullet.
1977b8f6 255
c567ac01 256Outline mode has to be reactivated in order for changes to the value
4bef9110
SE
257of this var to take effect."
258 :type 'string
259 :group 'allout)
fd5359c6 260(make-variable-buffer-local 'allout-plain-bullets-string)
8570ce80
SM
261;;;###autoload
262(put 'allout-plain-bullets-string 'safe-local-variable 'stringp)
fd5359c6 263;;;_ = allout-distinctive-bullets-string
d82979ea 264(defcustom allout-distinctive-bullets-string "*+-=>()[{}&!?#%\"X@$~_\\:;^"
19b84ba3 265 "*Persistent outline header bullets used to distinguish special topics.
1977b8f6 266
9179616f
DL
267These bullets are used to distinguish topics from the run-of-the-mill
268ones. They are not used in the standard topic headers created by
71296446 269the topic-opening, shifting, and rebulleting \(eg, on topic shift,
9179616f
DL
270topic paste, blanket rebulleting) routines, but are offered among the
271choices for rebulleting. They are not altered by the above automatic
272rebulleting, so they can be used to characterize topics, eg:
273
274 `?' question topics
275 `\(' parenthetic comment \(with a matching close paren inside)
276 `[' meta-note \(with a matching close ] inside)
d82979ea 277 `\"' a quotation
9179616f
DL
278 `=' value settings
279 `~' \"more or less\"
d82979ea 280 `^' see above
9179616f 281
d82979ea
EZ
282 ... for example. (`#' typically has a special meaning to the software,
283according to the value of `allout-numbered-bullet'.)
9179616f 284
fd5359c6 285See `allout-plain-bullets-string' for the selection of
9179616f 286alternating bullets.
1977b8f6 287
fd5359c6 288You must run `set-allout-regexp' in order for outline mode to
9179616f 289reconcile to changes of this value.
19b84ba3 290
353e2ef2 291DO NOT include the close-square-bracket, `]', on either of the bullet
4bef9110
SE
292strings."
293 :type 'string
294 :group 'allout)
fd5359c6 295(make-variable-buffer-local 'allout-distinctive-bullets-string)
8570ce80
SM
296;;;###autoload
297(put 'allout-distinctive-bullets-string 'safe-local-variable 'stringp)
1977b8f6 298
fd5359c6
MR
299;;;_ = allout-use-mode-specific-leader
300(defcustom allout-use-mode-specific-leader t
19b84ba3
RS
301 "*When non-nil, use mode-specific topic-header prefixes.
302
fd5359c6 303Allout outline mode will use the mode-specific `allout-mode-leaders'
19b84ba3 304and/or comment-start string, if any, to lead the topic prefix string,
353e2ef2 305so topic headers look like comments in the programming language.
19b84ba3
RS
306
307String values are used as they stand.
308
24190da5 309Value t means to first check for assoc value in `allout-mode-leaders'
19b84ba3 310alist, then use comment-start string, if any, then use default \(`.').
9179616f 311\(See note about use of comment-start strings, below.)
19b84ba3 312
fd5359c6 313Set to the symbol for either of `allout-mode-leaders' or
19b84ba3
RS
314`comment-start' to use only one of them, respectively.
315
6ed3e595 316Value nil means to always use the default \(`.').
19b84ba3
RS
317
318comment-start strings that do not end in spaces are tripled, and an
353e2ef2 319`_' underscore is tacked on the end, to distinguish them from regular
19b84ba3 320comment strings. comment-start strings that do end in spaces are not
9179616f 321tripled, but an underscore is substituted for the space. [This
19b84ba3 322presumes that the space is for appearance, not comment syntax. You
fd5359c6 323can use `allout-mode-leaders' to override this behavior, when
4bef9110 324incorrect.]"
71296446 325 :type '(choice (const t) (const nil) string
fd5359c6 326 (const allout-mode-leaders)
4bef9110
SE
327 (const comment-start))
328 :group 'allout)
8570ce80
SM
329;;;###autoload
330(put 'allout-use-mode-specific-leader 'safe-local-variable
da506f05
RS
331 '(lambda (x) (or (memq x '(t nil allout-mode-leaders comment-start))
332 (stringp x))))
fd5359c6
MR
333;;;_ = allout-mode-leaders
334(defvar allout-mode-leaders '()
335 "Specific allout-prefix leading strings per major modes.
19b84ba3 336
9179616f 337Entries will be used instead or in lieu of mode-specific
fd5359c6 338comment-start strings. See also `allout-use-mode-specific-leader'.
19b84ba3
RS
339
340If you're constructing a string that will comment-out outline
341structuring so it can be included in program code, append an extra
342character, like an \"_\" underscore, to distinguish the lead string
343from regular comments that start at bol.")
344
fd5359c6
MR
345;;;_ = allout-old-style-prefixes
346(defcustom allout-old-style-prefixes nil
aad94676 347 "*When non-nil, use only old-and-crusty `outline-mode' `*' topic prefixes.
19b84ba3
RS
348
349Non-nil restricts the topic creation and modification
c567ac01 350functions to asterix-padded prefixes, so they look exactly
e126900f 351like the original Emacs-outline style prefixes.
1977b8f6 352
c567ac01 353Whatever the setting of this variable, both old and new style prefixes
4bef9110
SE
354are always respected by the topic maneuvering functions."
355 :type 'boolean
356 :group 'allout)
fd5359c6 357(make-variable-buffer-local 'allout-old-style-prefixes)
8570ce80
SM
358;;;###autoload
359(put 'allout-old-style-prefixes 'safe-local-variable
64686e6d 360 (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
fd5359c6
MR
361;;;_ = allout-stylish-prefixes - alternating bullets
362(defcustom allout-stylish-prefixes t
19b84ba3 363 "*Do fancy stuff with topic prefix bullets according to level, etc.
c567ac01 364
19b84ba3
RS
365Non-nil enables topic creation, modification, and repositioning
366functions to vary the topic bullet char (the char that marks the topic
367depth) just preceding the start of the topic text) according to level.
353e2ef2 368Otherwise, only asterisks (`*') and distinctive bullets are used.
19b84ba3
RS
369
370This is how an outline can look (but sans indentation) with stylish
371prefixes:
c567ac01
RS
372
373 * Top level
374 .* A topic
375 . + One level 3 subtopic
376 . . One level 4 subtopic
19b84ba3 377 . . A second 4 subtopic
c567ac01 378 . + Another level 3 subtopic
19b84ba3
RS
379 . #1 A numbered level 4 subtopic
380 . #2 Another
381 . ! Another level 4 subtopic with a different distinctive bullet
382 . #4 And another numbered level 4 subtopic
c567ac01 383
19b84ba3
RS
384This would be an outline with stylish prefixes inhibited (but the
385numbered and other distinctive bullets retained):
c567ac01
RS
386
387 * Top level
388 .* A topic
19b84ba3
RS
389 . * One level 3 subtopic
390 . * One level 4 subtopic
391 . * A second 4 subtopic
392 . * Another level 3 subtopic
393 . #1 A numbered level 4 subtopic
394 . #2 Another
395 . ! Another level 4 subtopic with a different distinctive bullet
396 . #4 And another numbered level 4 subtopic
c567ac01
RS
397
398Stylish and constant prefixes (as well as old-style prefixes) are
399always respected by the topic maneuvering functions, regardless of
400this variable setting.
401
2265e017 402The setting of this var is not relevant when `allout-old-style-prefixes'
4bef9110
SE
403is non-nil."
404 :type 'boolean
405 :group 'allout)
fd5359c6 406(make-variable-buffer-local 'allout-stylish-prefixes)
8570ce80
SM
407;;;###autoload
408(put 'allout-stylish-prefixes 'safe-local-variable
64686e6d 409 (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
1977b8f6 410
fd5359c6
MR
411;;;_ = allout-numbered-bullet
412(defcustom allout-numbered-bullet "#"
19b84ba3
RS
413 "*String designating bullet of topics that have auto-numbering; nil for none.
414
a0776d6b 415Topics having this bullet have automatic maintenance of a sibling
19b84ba3 416sequence-number tacked on, just after the bullet. Conventionally set
c567ac01 417to \"#\", you can set it to a bullet of your choice. A nil value
4bef9110
SE
418disables numbering maintenance."
419 :type '(choice (const nil) string)
420 :group 'allout)
fd5359c6 421(make-variable-buffer-local 'allout-numbered-bullet)
8570ce80 422;;;###autoload
64686e6d
RS
423(put 'allout-numbered-bullet 'safe-local-variable
424 (if (fboundp 'string-or-null-p)
425 'string-or-null-p
426 '(lambda (x) (or (stringp x) (null x)))))
fd5359c6
MR
427;;;_ = allout-file-xref-bullet
428(defcustom allout-file-xref-bullet "@"
429 "*Bullet signifying file cross-references, for `allout-resolve-xref'.
19b84ba3 430
9179616f 431Set this var to the bullet you want to use for file cross-references."
4bef9110
SE
432 :type '(choice (const nil) string)
433 :group 'allout)
8570ce80 434;;;###autoload
64686e6d
RS
435(put 'allout-file-xref-bullet 'safe-local-variable
436 (if (fboundp 'string-or-null-p)
437 'string-or-null-p
438 '(lambda (x) (or (stringp x) (null x)))))
fd5359c6
MR
439;;;_ = allout-presentation-padding
440(defcustom allout-presentation-padding 2
9179616f
DL
441 "*Presentation-format white-space padding factor, for greater indent."
442 :type 'integer
443 :group 'allout)
444
fd5359c6 445(make-variable-buffer-local 'allout-presentation-padding)
8570ce80
SM
446;;;###autoload
447(put 'allout-presentation-padding 'safe-local-variable 'integerp)
9179616f 448
fd5359c6
MR
449;;;_ = allout-abbreviate-flattened-numbering
450(defcustom allout-abbreviate-flattened-numbering nil
451 "*If non-nil, `allout-flatten-exposed-to-buffer' abbreviates topic
9179616f
DL
452numbers to minimal amount with some context. Otherwise, entire
453numbers are always used."
454 :type 'boolean
455 :group 'allout)
456
c567ac01 457;;;_ + LaTeX formatting
fd5359c6
MR
458;;;_ - allout-number-pages
459(defcustom allout-number-pages nil
4bef9110
SE
460 "*Non-nil turns on page numbering for LaTeX formatting of an outline."
461 :type 'boolean
462 :group 'allout)
fd5359c6
MR
463;;;_ - allout-label-style
464(defcustom allout-label-style "\\large\\bf"
4bef9110
SE
465 "*Font and size of labels for LaTeX formatting of an outline."
466 :type 'string
467 :group 'allout)
fd5359c6
MR
468;;;_ - allout-head-line-style
469(defcustom allout-head-line-style "\\large\\sl "
4bef9110
SE
470 "*Font and size of entries for LaTeX formatting of an outline."
471 :type 'string
472 :group 'allout)
fd5359c6
MR
473;;;_ - allout-body-line-style
474(defcustom allout-body-line-style " "
4bef9110
SE
475 "*Font and size of entries for LaTeX formatting of an outline."
476 :type 'string
477 :group 'allout)
fd5359c6
MR
478;;;_ - allout-title-style
479(defcustom allout-title-style "\\Large\\bf"
4bef9110
SE
480 "*Font and size of titles for LaTeX formatting of an outline."
481 :type 'string
482 :group 'allout)
fd5359c6 483;;;_ - allout-title
d82979ea 484(defcustom allout-title '(or buffer-file-name (buffer-name))
c567ac01 485 "*Expression to be evaluated to determine the title for LaTeX
4bef9110
SE
486formatted copy."
487 :type 'sexp
488 :group 'allout)
fd5359c6
MR
489;;;_ - allout-line-skip
490(defcustom allout-line-skip ".05cm"
4bef9110
SE
491 "*Space between lines for LaTeX formatting of an outline."
492 :type 'string
493 :group 'allout)
fd5359c6
MR
494;;;_ - allout-indent
495(defcustom allout-indent ".3cm"
4bef9110
SE
496 "*LaTeX formatted depth-indent spacing."
497 :type 'string
498 :group 'allout)
c567ac01 499
d82979ea 500;;;_ + Topic encryption
8570ce80
SM
501;;;_ = allout-encryption group
502(defgroup allout-encryption nil
503 "Settings for topic encryption features of allout outliner."
504 :group 'allout)
d82979ea
EZ
505;;;_ = allout-topic-encryption-bullet
506(defcustom allout-topic-encryption-bullet "~"
507 "*Bullet signifying encryption of the entry's body."
508 :type '(choice (const nil) string)
8570ce80
SM
509 :version "22.0"
510 :group 'allout-encryption)
0949617b
EZ
511;;;_ = allout-passphrase-verifier-handling
512(defcustom allout-passphrase-verifier-handling t
513 "*Enable use of symmetric encryption passphrase verifier if non-nil.
d82979ea
EZ
514
515See the docstring for the `allout-enable-file-variable-adjustment'
516variable for details about allout ajustment of file variables."
0949617b 517 :type 'boolean
8570ce80
SM
518 :version "22.0"
519 :group 'allout-encryption)
0949617b
EZ
520(make-variable-buffer-local 'allout-passphrase-verifier-handling)
521;;;_ = allout-passphrase-hint-handling
522(defcustom allout-passphrase-hint-handling 'always
523 "*Dictate outline encryption passphrase reminder handling:
d82979ea
EZ
524
525 always - always show reminder when prompting
0949617b
EZ
526 needed - show reminder on passphrase entry failure
527 disabled - never present or adjust reminder
d82979ea
EZ
528
529See the docstring for the `allout-enable-file-variable-adjustment'
530variable for details about allout ajustment of file variables."
531 :type '(choice (const always)
532 (const needed)
d82979ea 533 (const disabled))
8570ce80
SM
534 :version "22.0"
535 :group 'allout-encryption)
0949617b 536(make-variable-buffer-local 'allout-passphrase-hint-handling)
d82979ea 537;;;_ = allout-encrypt-unencrypted-on-saves
96b83743 538(defcustom allout-encrypt-unencrypted-on-saves t
d82979ea
EZ
539 "*When saving, should topics pending encryption be encrypted?
540
541The idea is to prevent file-system exposure of any un-encrypted stuff, and
542mostly covers both deliberate file writes and auto-saves.
543
544 - Yes: encrypt all topics pending encryption, even if it's the one
545 currently being edited. \(In that case, the currently edited topic
546 will be automatically decrypted before any user interaction, so they
547 can continue editing but the copy on the file system will be
548 encrypted.)
549 Auto-saves will use the \"All except current topic\" mode if this
550 one is selected, to avoid practical difficulties - see below.
551 - All except current topic: skip the topic currently being edited, even if
552 it's pending encryption. This may expose the current topic on the
553 file sytem, but avoids the nuisance of prompts for the encryption
0949617b 554 passphrase in the middle of editing for, eg, autosaves.
d82979ea
EZ
555 This mode is used for auto-saves for both this option and \"Yes\".
556 - No: leave it to the user to encrypt any unencrypted topics.
557
558For practical reasons, auto-saves always use the 'except-current policy
0949617b
EZ
559when auto-encryption is enabled. \(Otherwise, spurious passphrase prompts
560and unavoidable timing collisions are too disruptive.) If security for a
561file requires that even the current topic is never auto-saved in the clear,
d82979ea
EZ
562disable auto-saves for that file."
563
564 :type '(choice (const :tag "Yes" t)
565 (const :tag "All except current topic" except-current)
566 (const :tag "No" nil))
8570ce80
SM
567 :version "22.0"
568 :group 'allout-encryption)
d82979ea
EZ
569(make-variable-buffer-local 'allout-encrypt-unencrypted-on-saves)
570
01fc9422
EZ
571;;;_ + Developer
572;;;_ = allout-developer group
573(defgroup allout-developer nil
574 "Settings for topic encryption features of allout outliner."
575 :group 'allout)
576;;;_ = allout-run-unit-tests-on-load
577(defcustom allout-run-unit-tests-on-load nil
578 "*When non-nil, unit tests will be run at end of loading the allout module.
579
580Generally, allout code developers are the only ones who'll want to set this.
581
582\(If set, this makes it an even better practice to exercise changes by
5204800f
NR
583doing byte-compilation with a repeat count, so the file is loaded after
584compilation.)
01fc9422
EZ
585
586See `allout-run-unit-tests' to see what's run."
587 :type 'boolean
588 :group 'allout-developer)
589
c567ac01
RS
590;;;_ + Miscellaneous customization
591
fd5359c6 592;;;_ = allout-command-prefix
96b83743
EZ
593(defcustom allout-command-prefix "\C-c "
594 "*Key sequence to be used as prefix for outline mode command key bindings.
595
596Default is '\C-c<space>'; just '\C-c' is more short-and-sweet, if you're
597willing to let allout use a bunch of \C-c keybindings."
9179616f
DL
598 :type 'string
599 :group 'allout)
600
fd5359c6
MR
601;;;_ = allout-keybindings-list
602;;; You have to reactivate allout-mode - `(allout-mode t)' - to
c567ac01 603;;; institute changes to this var.
fd5359c6 604(defvar allout-keybindings-list ()
2265e017 605 "*List of `allout-mode' key / function bindings, for `allout-mode-map'.
19b84ba3 606
2265e017 607String or vector key will be prefaced with `allout-command-prefix',
9179616f 608unless optional third, non-nil element is present.")
fd5359c6 609(setq allout-keybindings-list
1977b8f6
RS
610 '(
611 ; Motion commands:
fd5359c6
MR
612 ("\C-n" allout-next-visible-heading)
613 ("\C-p" allout-previous-visible-heading)
614 ("\C-u" allout-up-current-level)
615 ("\C-f" allout-forward-current-level)
616 ("\C-b" allout-backward-current-level)
617 ("\C-a" allout-beginning-of-current-entry)
d82979ea 618 ("\C-e" allout-end-of-entry)
1977b8f6 619 ; Exposure commands:
fd5359c6
MR
620 ("\C-i" allout-show-children)
621 ("\C-s" allout-show-current-subtree)
622 ("\C-h" allout-hide-current-subtree)
d82979ea 623 ("h" allout-hide-current-subtree)
fd5359c6
MR
624 ("\C-o" allout-show-current-entry)
625 ("!" allout-show-all)
d82979ea 626 ("x" allout-toggle-current-subtree-encryption)
1977b8f6 627 ; Alteration commands:
fd5359c6
MR
628 (" " allout-open-sibtopic)
629 ("." allout-open-subtopic)
630 ("," allout-open-supertopic)
631 ("'" allout-shift-in)
632 (">" allout-shift-in)
633 ("<" allout-shift-out)
634 ("\C-m" allout-rebullet-topic)
635 ("*" allout-rebullet-current-heading)
636 ("#" allout-number-siblings)
637 ("\C-k" allout-kill-line t)
638 ("\C-y" allout-yank t)
639 ("\M-y" allout-yank-pop t)
640 ("\C-k" allout-kill-topic)
1977b8f6 641 ; Miscellaneous commands:
fd5359c6
MR
642 ;([?\C-\ ] allout-mark-topic)
643 ("@" allout-resolve-xref)
644 ("=c" allout-copy-exposed-to-buffer)
645 ("=i" allout-indented-exposed-to-buffer)
646 ("=t" allout-latexify-exposed)
647 ("=p" allout-flatten-exposed-to-buffer)))
648
01fc9422
EZ
649;;;_ = allout-inhibit-auto-fill
650(defcustom allout-inhibit-auto-fill nil
651 "*If non-nil, auto-fill will be inhibited in the allout buffers.
652
653You can customize this setting to set it for all allout buffers, or set it
654in individual buffers if you want to inhibit auto-fill only in particular
655buffers. \(You could use a function on `allout-mode-hook' to inhibit
656auto-fill according, eg, to the major mode.\)
657
658If you don't set this and auto-fill-mode is enabled, allout will use the
659value that `normal-auto-fill-function', if any, when allout mode starts, or
660else allout's special hanging-indent maintaining auto-fill function,
661`allout-auto-fill'."
662 :type 'boolean
663 :group 'allout)
664(make-variable-buffer-local 'allout-inhibit-auto-fill)
665
fd5359c6
MR
666;;;_ = allout-use-hanging-indents
667(defcustom allout-use-hanging-indents t
19b84ba3
RS
668 "*If non-nil, topic body text auto-indent defaults to indent of the header.
669Ie, it is indented to be just past the header prefix. This is
670relevant mostly for use with indented-text-mode, or other situations
96b83743 671where auto-fill occurs."
4bef9110
SE
672 :type 'boolean
673 :group 'allout)
fd5359c6 674(make-variable-buffer-local 'allout-use-hanging-indents)
8570ce80
SM
675;;;###autoload
676(put 'allout-use-hanging-indents 'safe-local-variable
64686e6d 677 (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
c567ac01 678
fd5359c6
MR
679;;;_ = allout-reindent-bodies
680(defcustom allout-reindent-bodies (if allout-use-hanging-indents
8d118843 681 'text)
19b84ba3 682 "*Non-nil enables auto-adjust of topic body hanging indent with depth shifts.
1977b8f6 683
8d118843
RS
684When active, topic body lines that are indented even with or beyond
685their topic header are reindented to correspond with depth shifts of
686the header.
687
24190da5 688A value of t enables reindent in non-programming-code buffers, ie
8d118843 689those that do not have the variable `comment-start' set. A value of
4bef9110
SE
690`force' enables reindent whether or not `comment-start' is set."
691 :type '(choice (const nil) (const t) (const text) (const force))
692 :group 'allout)
8d118843 693
fd5359c6 694(make-variable-buffer-local 'allout-reindent-bodies)
8570ce80
SM
695;;;###autoload
696(put 'allout-reindent-bodies 'safe-local-variable
da506f05 697 '(lambda (x) (memq x '(nil t text force))))
c567ac01 698
d82979ea
EZ
699;;;_ = allout-enable-file-variable-adjustment
700(defcustom allout-enable-file-variable-adjustment t
0949617b 701 "*If non-nil, some allout outline actions edit Emacs local file var text.
d82979ea
EZ
702
703This can range from changes to existing entries, addition of new ones,
704and creation of a new local variables section when necessary.
19b84ba3 705
d82979ea
EZ
706Emacs file variables adjustments are also inhibited if `enable-local-variables'
707is nil.
c567ac01 708
d82979ea 709Operations potentially causing edits include allout encryption routines.
69027dea 710For details, see `allout-toggle-current-subtree-encryption's docstring."
4bef9110
SE
711 :type 'boolean
712 :group 'allout)
d82979ea 713(make-variable-buffer-local 'allout-enable-file-variable-adjustment)
c567ac01 714
19b84ba3 715;;;_* CODE - no user customizations below.
c567ac01 716
9179616f
DL
717;;;_ #1 Internal Outline Formatting and Configuration
718;;;_ : Version
fd5359c6 719;;;_ = allout-version
4034b0e2 720(defvar allout-version "2.2.1"
0949617b 721 "Version of currently loaded outline package. \(allout.el)")
fd5359c6
MR
722;;;_ > allout-version
723(defun allout-version (&optional here)
19b84ba3
RS
724 "Return string describing the loaded outline version."
725 (interactive "P")
fd5359c6 726 (let ((msg (concat "Allout Outline Mode v " allout-version)))
eac9cf5f 727 (if here (insert msg))
19b84ba3
RS
728 (message "%s" msg)
729 msg))
69027dea
EZ
730;;;_ : Mode activation (defined here because it's referenced early)
731;;;_ = allout-mode
732(defvar allout-mode nil "Allout outline mode minor-mode flag.")
733(make-variable-buffer-local 'allout-mode)
4034b0e2
LT
734;;;_ = allout-layout nil
735(defvar allout-layout nil ; LEAVE GLOBAL VALUE NIL - see docstring.
736 "Buffer-specific setting for allout layout.
737
738In buffers where this is non-nil \(and if `allout-init' has been run, to
739enable this behavior), `allout-mode' will be automatically activated. The
740layout dictated by the value will be used to set the initial exposure when
741`allout-mode' is activated.
742
743\*You should not setq-default this variable non-nil unless you want every
744visited file to be treated as an allout file.*
745
746The value would typically be set by a file local variable. For
747example, the following lines at the bottom of an Emacs Lisp file:
748
749;;;Local variables:
750;;;allout-layout: \(0 : -1 -1 0)
751;;;End:
752
753dictate activation of `allout-mode' mode when the file is visited
754\(presuming allout-init was already run), followed by the
755equivalent of `\(allout-expose-topic 0 : -1 -1 0)'. \(This is
756the layout used for the allout.el source file.)
757
758`allout-default-layout' describes the specification format.
759`allout-layout' can additionally have the value `t', in which
760case the value of `allout-default-layout' is used.")
761(make-variable-buffer-local 'allout-layout)
b1a569b5 762;;;###autoload
8570ce80 763(put 'allout-layout 'safe-local-variable
da506f05 764 '(lambda (x) (or (numberp x) (listp x) (memq x '(: * + -)))))
4034b0e2 765
9179616f 766;;;_ : Topic header format
fd5359c6
MR
767;;;_ = allout-regexp
768(defvar allout-regexp ""
c567ac01 769 "*Regular expression to match the beginning of a heading line.
19b84ba3 770
c567ac01
RS
771Any line whose beginning matches this regexp is considered a
772heading. This var is set according to the user configuration vars
2265e017 773by `set-allout-regexp'.")
fd5359c6
MR
774(make-variable-buffer-local 'allout-regexp)
775;;;_ = allout-bullets-string
776(defvar allout-bullets-string ""
19b84ba3
RS
777 "A string dictating the valid set of outline topic bullets.
778
fd5359c6
MR
779This var should *not* be set by the user - it is set by `set-allout-regexp',
780and is produced from the elements of `allout-plain-bullets-string'
781and `allout-distinctive-bullets-string'.")
782(make-variable-buffer-local 'allout-bullets-string)
783;;;_ = allout-bullets-string-len
784(defvar allout-bullets-string-len 0
2265e017 785 "Length of current buffers' `allout-plain-bullets-string'.")
fd5359c6
MR
786(make-variable-buffer-local 'allout-bullets-string-len)
787;;;_ = allout-line-boundary-regexp
788(defvar allout-line-boundary-regexp ()
aad94676 789 "`allout-regexp' with outline style beginning-of-line anchor.
19b84ba3 790
96b83743
EZ
791This is properly set when `allout-regexp' is produced by
792`set-allout-regexp', so that (match-beginning 2) and (match-end
7932) delimit the prefix.")
fd5359c6
MR
794(make-variable-buffer-local 'allout-line-boundary-regexp)
795;;;_ = allout-bob-regexp
796(defvar allout-bob-regexp ()
2265e017 797 "Like `allout-line-boundary-regexp', for headers at beginning of buffer.
9179616f 798\(match-beginning 2) and \(match-end 2) delimit the prefix.")
fd5359c6
MR
799(make-variable-buffer-local 'allout-bob-regexp)
800;;;_ = allout-header-subtraction
801(defvar allout-header-subtraction (1- (length allout-header-prefix))
802 "Allout-header prefix length to subtract when computing topic depth.")
803(make-variable-buffer-local 'allout-header-subtraction)
804;;;_ = allout-plain-bullets-string-len
805(defvar allout-plain-bullets-string-len (length allout-plain-bullets-string)
2265e017 806 "Length of `allout-plain-bullets-string', updated by `set-allout-regexp'.")
fd5359c6
MR
807(make-variable-buffer-local 'allout-plain-bullets-string-len)
808
809
810;;;_ X allout-reset-header-lead (header-lead)
811(defun allout-reset-header-lead (header-lead)
c567ac01 812 "*Reset the leading string used to identify topic headers."
1977b8f6 813 (interactive "sNew lead string: ")
fd5359c6
MR
814 (setq allout-header-prefix header-lead)
815 (setq allout-header-subtraction (1- (length allout-header-prefix)))
816 (set-allout-regexp))
817;;;_ X allout-lead-with-comment-string (header-lead)
818(defun allout-lead-with-comment-string (&optional header-lead)
19b84ba3
RS
819 "*Set the topic-header leading string to specified string.
820
821Useful when for encapsulating outline structure in programming
822language comments. Returns the leading string."
1977b8f6
RS
823
824 (interactive "P")
825 (if (not (stringp header-lead))
826 (setq header-lead (read-string
827 "String prefix for topic headers: ")))
fd5359c6
MR
828 (setq allout-reindent-bodies nil)
829 (allout-reset-header-lead header-lead)
1977b8f6 830 header-lead)
fd5359c6
MR
831;;;_ > allout-infer-header-lead ()
832(defun allout-infer-header-lead ()
833 "Determine appropriate `allout-header-prefix'.
19b84ba3
RS
834
835Works according to settings of:
836
8d118843 837 `comment-start'
fd5359c6
MR
838 `allout-header-prefix' (default)
839 `allout-use-mode-specific-leader'
840and `allout-mode-leaders'.
19b84ba3 841
fd5359c6 842Apply this via \(re)activation of `allout-mode', rather than
8d118843 843invoking it directly."
fd5359c6
MR
844 (let* ((use-leader (and (boundp 'allout-use-mode-specific-leader)
845 (if (or (stringp allout-use-mode-specific-leader)
846 (memq allout-use-mode-specific-leader
847 '(allout-mode-leaders
19b84ba3
RS
848 comment-start
849 t)))
fd5359c6 850 allout-use-mode-specific-leader
19b84ba3
RS
851 ;; Oops - garbled value, equate with effect of 't:
852 t)))
853 (leader
854 (cond
855 ((not use-leader) nil)
856 ;; Use the explicitly designated leader:
857 ((stringp use-leader) use-leader)
fd5359c6 858 (t (or (and (memq use-leader '(t allout-mode-leaders))
19b84ba3 859 ;; Get it from outline mode leaders?
fd5359c6
MR
860 (cdr (assq major-mode allout-mode-leaders)))
861 ;; ... didn't get from allout-mode-leaders...
19b84ba3
RS
862 (and (memq use-leader '(t comment-start))
863 comment-start
864 ;; Use comment-start, maybe tripled, and with
353e2ef2 865 ;; underscore:
19b84ba3
RS
866 (concat
867 (if (string= " "
868 (substring comment-start
869 (1- (length comment-start))))
870 ;; Use comment-start, sans trailing space:
871 (substring comment-start 0 -1)
872 (concat comment-start comment-start comment-start))
873 ;; ... and append underscore, whichever:
874 "_")))))))
875 (if (not leader)
876 nil
fd5359c6 877 (if (string= leader allout-header-prefix)
19b84ba3 878 nil ; no change, nothing to do.
fd5359c6
MR
879 (setq allout-header-prefix leader)
880 allout-header-prefix))))
881;;;_ > allout-infer-body-reindent ()
882(defun allout-infer-body-reindent ()
883 "Determine proper setting for `allout-reindent-bodies'.
8d118843 884
fd5359c6 885Depends on default setting of `allout-reindent-bodies' \(which see)
8d118843
RS
886and presence of setting for `comment-start', to tell whether the
887file is programming code."
fd5359c6 888 (if (and allout-reindent-bodies
8d118843 889 comment-start
fd5359c6
MR
890 (not (eq 'force allout-reindent-bodies)))
891 (setq allout-reindent-bodies nil)))
892;;;_ > set-allout-regexp ()
893(defun set-allout-regexp ()
19b84ba3
RS
894 "Generate proper topic-header regexp form for outline functions.
895
fd5359c6
MR
896Works with respect to `allout-plain-bullets-string' and
897`allout-distinctive-bullets-string'."
1977b8f6
RS
898
899 (interactive)
fd5359c6
MR
900 ;; Derive allout-bullets-string from user configured components:
901 (setq allout-bullets-string "")
902 (let ((strings (list 'allout-plain-bullets-string
903 'allout-distinctive-bullets-string
904 'allout-primary-bullet))
1977b8f6
RS
905 cur-string
906 cur-len
c567ac01 907 cur-char
96b83743 908 index)
1977b8f6 909 (while strings
96b83743 910 (setq index 0)
1977b8f6
RS
911 (setq cur-len (length (setq cur-string (symbol-value (car strings)))))
912 (while (< index cur-len)
913 (setq cur-char (aref cur-string index))
fd5359c6
MR
914 (setq allout-bullets-string
915 (concat allout-bullets-string
1977b8f6
RS
916 (cond
917 ; Single dash would denote a
918 ; sequence, repeated denotes
919 ; a dash:
920 ((eq cur-char ?-) "--")
921 ; literal close-square-bracket
922 ; doesn't work right in the
923 ; expr, exclude it:
924 ((eq cur-char ?\]) "")
925 (t (regexp-quote (char-to-string cur-char))))))
926 (setq index (1+ index)))
927 (setq strings (cdr strings)))
928 )
fd5359c6
MR
929 ;; Derive next for repeated use in allout-pending-bullet:
930 (setq allout-plain-bullets-string-len (length allout-plain-bullets-string))
931 (setq allout-header-subtraction (1- (length allout-header-prefix)))
932 ;; Produce the new allout-regexp:
933 (setq allout-regexp (concat "\\(\\"
934 allout-header-prefix
1977b8f6 935 "[ \t]*["
fd5359c6 936 allout-bullets-string
1977b8f6 937 "]\\)\\|\\"
fd5359c6 938 allout-primary-bullet
1977b8f6 939 "+\\|\^l"))
fd5359c6 940 (setq allout-line-boundary-regexp
96b83743 941 (concat "\\(\n\\)\\(" allout-regexp "\\)"))
fd5359c6
MR
942 (setq allout-bob-regexp
943 (concat "\\(\\`\\)\\(" allout-regexp "\\)"))
1977b8f6 944 )
9179616f 945;;;_ : Key bindings
fd5359c6
MR
946;;;_ = allout-mode-map
947(defvar allout-mode-map nil "Keybindings for (allout) outline minor mode.")
948;;;_ > produce-allout-mode-map (keymap-alist &optional base-map)
949(defun produce-allout-mode-map (keymap-list &optional base-map)
e126900f 950 "Produce keymap for use as allout-mode-map, from KEYMAP-LIST.
19b84ba3
RS
951
952Built on top of optional BASE-MAP, or empty sparse map if none specified.
fd5359c6 953See doc string for allout-keybindings-list for format of binding list."
9179616f 954 (let ((map (or base-map (make-sparse-keymap)))
fd5359c6 955 (pref (list allout-command-prefix)))
9179616f
DL
956 (mapcar (function
957 (lambda (cell)
958 (let ((add-pref (null (cdr (cdr cell))))
959 (key-suff (list (car cell))))
960 (apply 'define-key
961 (list map
962 (apply 'concat (if add-pref
963 (append pref key-suff)
964 key-suff))
965 (car (cdr cell)))))))
19b84ba3
RS
966 keymap-list)
967 map))
d82979ea
EZ
968;;;_ = allout-prior-bindings - being deprecated.
969(defvar allout-prior-bindings nil
970 "Variable for use in V18, with allout-added-bindings, for
971resurrecting, on mode deactivation, bindings that existed before
972activation. Being deprecated.")
973;;;_ = allout-added-bindings - being deprecated
974(defvar allout-added-bindings nil
975 "Variable for use in V18, with allout-prior-bindings, for
976resurrecting, on mode deactivation, bindings that existed before
977activation. Being deprecated.")
9179616f 978;;;_ : Menu bar
95c12694
RS
979(defvar allout-mode-exposure-menu)
980(defvar allout-mode-editing-menu)
981(defvar allout-mode-navigation-menu)
982(defvar allout-mode-misc-menu)
fd5359c6 983(defun produce-allout-mode-menubar-entries ()
9179616f 984 (require 'easymenu)
fd5359c6
MR
985 (easy-menu-define allout-mode-exposure-menu
986 allout-mode-map
9179616f
DL
987 "Allout outline exposure menu."
988 '("Exposure"
fd5359c6
MR
989 ["Show Entry" allout-show-current-entry t]
990 ["Show Children" allout-show-children t]
991 ["Show Subtree" allout-show-current-subtree t]
992 ["Hide Subtree" allout-hide-current-subtree t]
993 ["Hide Leaves" allout-hide-current-leaves t]
9179616f 994 "----"
fd5359c6
MR
995 ["Show All" allout-show-all t]))
996 (easy-menu-define allout-mode-editing-menu
997 allout-mode-map
9179616f
DL
998 "Allout outline editing menu."
999 '("Headings"
fd5359c6
MR
1000 ["Open Sibling" allout-open-sibtopic t]
1001 ["Open Subtopic" allout-open-subtopic t]
1002 ["Open Supertopic" allout-open-supertopic t]
9179616f 1003 "----"
fd5359c6
MR
1004 ["Shift Topic In" allout-shift-in t]
1005 ["Shift Topic Out" allout-shift-out t]
1006 ["Rebullet Topic" allout-rebullet-topic t]
1007 ["Rebullet Heading" allout-rebullet-current-heading t]
d82979ea
EZ
1008 ["Number Siblings" allout-number-siblings t]
1009 "----"
1010 ["Toggle Topic Encryption"
1011 allout-toggle-current-subtree-encryption
1012 (> (allout-current-depth) 1)]))
fd5359c6
MR
1013 (easy-menu-define allout-mode-navigation-menu
1014 allout-mode-map
9179616f
DL
1015 "Allout outline navigation menu."
1016 '("Navigation"
fd5359c6 1017 ["Next Visible Heading" allout-next-visible-heading t]
9179616f 1018 ["Previous Visible Heading"
fd5359c6 1019 allout-previous-visible-heading t]
9179616f 1020 "----"
fd5359c6
MR
1021 ["Up Level" allout-up-current-level t]
1022 ["Forward Current Level" allout-forward-current-level t]
9179616f 1023 ["Backward Current Level"
fd5359c6 1024 allout-backward-current-level t]
9179616f
DL
1025 "----"
1026 ["Beginning of Entry"
fd5359c6 1027 allout-beginning-of-current-entry t]
d82979ea 1028 ["End of Entry" allout-end-of-entry t]
fd5359c6
MR
1029 ["End of Subtree" allout-end-of-current-subtree t]))
1030 (easy-menu-define allout-mode-misc-menu
1031 allout-mode-map
9179616f
DL
1032 "Allout outlines miscellaneous bindings."
1033 '("Misc"
fd5359c6 1034 ["Version" allout-version t]
9179616f 1035 "----"
fd5359c6 1036 ["Duplicate Exposed" allout-copy-exposed-to-buffer t]
9179616f 1037 ["Duplicate Exposed, numbered"
71296446 1038 allout-flatten-exposed-to-buffer t]
9179616f 1039 ["Duplicate Exposed, indented"
71296446 1040 allout-indented-exposed-to-buffer t]
9179616f 1041 "----"
fd5359c6
MR
1042 ["Set Header Lead" allout-reset-header-lead t]
1043 ["Set New Exposure" allout-expose-topic t])))
01fc9422 1044;;;_ : Allout Modal-Variables Utilities
fd5359c6
MR
1045;;;_ = allout-mode-prior-settings
1046(defvar allout-mode-prior-settings nil
01fc9422 1047 "Internal `allout-mode' use; settings to be resumed on mode deactivation.
1977b8f6 1048
01fc9422
EZ
1049See `allout-add-resumptions' and `allout-do-resumptions'.")
1050(make-variable-buffer-local 'allout-mode-prior-settings)
1051;;;_ > allout-add-resumptions (&rest pairs)
1052(defun allout-add-resumptions (&rest pairs)
1053 "Set name/value pairs.
1054
1055Old settings are preserved for later resumption using `allout-do-resumptions'.
1056
1057The pairs are lists whose car is the name of the variable and car of the
1058cdr is the new value: '(some-var some-value)'.
1059
1060The new value is set as a buffer local.
1061
1062If the variable was not previously buffer-local, then that is noted and the
1063`allout-do-resumptions' will just `kill-local-variable' of that binding.
1064
1065If it previously was buffer-local, the old value is noted and resurrected
1066by `allout-do-resumptions'. \(If the local value was previously void, then
1067it is left as nil on resumption.\)
1068
1069The settings are stored on `allout-mode-prior-settings'."
1070 (while pairs
1071 (let* ((pair (pop pairs))
1072 (name (car pair))
1073 (value (cadr pair)))
1074 (if (not (symbolp name))
1075 (error "Pair's name, %S, must be a symbol, not %s"
1076 name (type-of name)))
1077 (when (not (assoc name allout-mode-prior-settings))
1078 ;; Not already added as a resumption, create the prior setting entry.
1079 (if (local-variable-p name)
1080 ;; is already local variable - preserve the prior value:
1081 (push (list name (condition-case err
1082 (symbol-value name)
1083 (void-variable nil)))
1084 allout-mode-prior-settings)
1085 ;; wasn't local variable, indicate so for resumption by killing
1086 ;; local value, and make it local:
1087 (push (list name) allout-mode-prior-settings)
1088 (make-local-variable name)))
1089 (set name value))))
1090;;;_ > allout-do-resumptions ()
1091(defun allout-do-resumptions ()
1092 "Resume all name/value settings registered by `allout-add-resumptions'.
1093
1094This is used when concluding allout-mode, to resume selected variables to
1095their settings before allout-mode was started."
1096
1097 (while allout-mode-prior-settings
1098 (let* ((pair (pop allout-mode-prior-settings))
1099 (name (car pair))
1100 (value-cell (cdr pair)))
1101 (if (not value-cell)
1102 ;; Prior value was global:
1103 (kill-local-variable name)
1104 ;; Prior value was explicit:
1105 (set name (car value-cell))))))
9179616f 1106;;;_ : Mode-specific incidentals
fd5359c6 1107;;;_ > allout-unprotected (expr)
d82979ea 1108(defmacro allout-unprotected (expr)
96b83743 1109 "Enable internal outline operations to alter invisible text."
0458fa80
EZ
1110 `(let ((inhibit-read-only t)
1111 (inhibit-field-text-motion t))
96b83743
EZ
1112 ,expr))
1113;;;_ = allout-mode-hook
1114(defvar allout-mode-hook nil
1115 "*Hook that's run when allout mode starts.")
01fc9422
EZ
1116;;;_ = allout-mode-deactivate-hook
1117(defvar allout-mode-deactivate-hook nil
1118 "*Hook that's run when allout mode ends.")
1119;;;_ = allout-exposure-category
1120(defvar allout-exposure-category nil
1121 "Symbol for use as allout invisible-text overlay category.")
da506f05 1122;;;_ x allout-view-change-hook
96b83743 1123(defvar allout-view-change-hook nil
da506f05
RS
1124 "*\(Deprecated\) Hook that's run after allout outline exposure changes.
1125
1126Switch to using `allout-exposure-change-hook' instead. Both
64686e6d
RS
1127variables are currently respected, but this one will be ignored
1128in a subsequent allout version.")
da506f05
RS
1129;;;_ = allout-exposure-change-hook
1130(defvar allout-exposure-change-hook nil
1131 "*Hook that's run after allout outline exposure changes.
1132
1133This variable will replace `allout-view-change-hook' in a subsequent allout
64686e6d 1134version, though both are currently respected.")
96b83743
EZ
1135
1136;;;_ = allout-outside-normal-auto-fill-function
1137(defvar allout-outside-normal-auto-fill-function nil
1138 "Value of normal-auto-fill-function outside of allout mode.
1139
1140Used by allout-auto-fill to do the mandated normal-auto-fill-function
1141wrapped within allout's automatic fill-prefix setting.")
1142(make-variable-buffer-local 'allout-outside-normal-auto-fill-function)
19b84ba3 1143;;;_ = file-var-bug hack
fd5359c6 1144(defvar allout-v18/19-file-var-hack nil
c567ac01 1145 "Horrible hack used to prevent invalid multiple triggering of outline
2265e017 1146mode from prop-line file-var activation. Used by `allout-mode' function
c567ac01 1147to track repeats.")
0949617b
EZ
1148;;;_ = allout-passphrase-verifier-string
1149(defvar allout-passphrase-verifier-string nil
1150 "Setting used to test solicited encryption passphrases against the one
1151already associated with a file.
1152
1153It consists of an encrypted random string useful only to verify that a
1154passphrase entered by the user is effective for decryption. The passphrase
1155itself is \*not* recorded in the file anywhere, and the encrypted contents
1156are random binary characters to avoid exposing greater susceptibility to
1157search attacks.
d82979ea
EZ
1158
1159The verifier string is retained as an Emacs file variable, as well as in
1160the emacs buffer state, if file variable adjustments are enabled. See
1161`allout-enable-file-variable-adjustment' for details about that.")
0949617b 1162(make-variable-buffer-local 'allout-passphrase-verifier-string)
8570ce80 1163;;;###autoload
7e5d77dc 1164(put 'allout-passphrase-verifier-string 'safe-local-variable 'stringp)
0949617b
EZ
1165;;;_ = allout-passphrase-hint-string
1166(defvar allout-passphrase-hint-string ""
1167 "Variable used to retain reminder string for file's encryption passphrase.
d82979ea 1168
0949617b 1169See the description of `allout-passphrase-hint-handling' for details about how
d82979ea
EZ
1170the reminder is deployed.
1171
1172The hint is retained as an Emacs file variable, as well as in the emacs buffer
1173state, if file variable adjustments are enabled. See
1174`allout-enable-file-variable-adjustment' for details about that.")
0949617b
EZ
1175(make-variable-buffer-local 'allout-passphrase-hint-string)
1176(setq-default allout-passphrase-hint-string "")
8570ce80
SM
1177;;;###autoload
1178(put 'allout-passphrase-hint-string 'safe-local-variable 'stringp)
d82979ea
EZ
1179;;;_ = allout-after-save-decrypt
1180(defvar allout-after-save-decrypt nil
1181 "Internal variable, is nil or has the value of two points:
1182
1183 - the location of a topic to be decrypted after saving is done
1184 - where to situate the cursor after the decryption is performed
1185
1186This is used to decrypt the topic that was currently being edited, if it
1187was encrypted automatically as part of a file write or autosave.")
1188(make-variable-buffer-local 'allout-after-save-decrypt)
69027dea
EZ
1189;;;_ > allout-mode-p ()
1190;; Must define this macro above any uses, or byte compilation will lack
1191;; proper def, if file isn't loaded - eg, during emacs build!
1192(defmacro allout-mode-p ()
1193 "Return t if `allout-mode' is active in current buffer."
1194 'allout-mode)
d82979ea
EZ
1195;;;_ > allout-write-file-hook-handler ()
1196(defun allout-write-file-hook-handler ()
1197 "Implement `allout-encrypt-unencrypted-on-saves' policy for file writes."
1198
0949617b
EZ
1199 (if (or (not (allout-mode-p))
1200 (not (boundp 'allout-encrypt-unencrypted-on-saves))
d82979ea
EZ
1201 (not allout-encrypt-unencrypted-on-saves))
1202 nil
1203 (let ((except-mark (and (equal allout-encrypt-unencrypted-on-saves
1204 'except-current)
1205 (point-marker))))
1206 (if (save-excursion (goto-char (point-min))
1207 (allout-next-topic-pending-encryption except-mark))
1208 (progn
1209 (message "auto-encrypting pending topics")
96b83743 1210 (sit-for 0)
d82979ea
EZ
1211 (condition-case failure
1212 (setq allout-after-save-decrypt
1213 (allout-encrypt-decrypted except-mark))
1214 (error (progn
1215 (message
1216 "allout-write-file-hook-handler suppressing error %s"
1217 failure)
1218 (sit-for 2))))))
1219 ))
1220 nil)
1221;;;_ > allout-auto-save-hook-handler ()
1222(defun allout-auto-save-hook-handler ()
69027dea 1223 "Implement `allout-encrypt-unencrypted-on-saves' policy for auto save."
d82979ea 1224
0949617b 1225 (if (and (allout-mode-p) allout-encrypt-unencrypted-on-saves)
d82979ea
EZ
1226 ;; Always implement 'except-current policy when enabled.
1227 (let ((allout-encrypt-unencrypted-on-saves 'except-current))
1228 (allout-write-file-hook-handler))))
1229;;;_ > allout-after-saves-handler ()
1230(defun allout-after-saves-handler ()
1231 "Decrypt topic encrypted for save, if it's currently being edited.
1232
1233Ie, if it was pending encryption and contained the point in its body before
1234the save.
1235
1236We use values stored in `allout-after-save-decrypt' to locate the topic
1237and the place for the cursor after the decryption is done."
1238 (if (not (and (allout-mode-p)
1239 (boundp 'allout-after-save-decrypt)
1240 allout-after-save-decrypt))
1241 t
1242 (goto-char (car allout-after-save-decrypt))
1243 (let ((was-modified (buffer-modified-p)))
69027dea 1244 (allout-toggle-subtree-encryption)
d82979ea
EZ
1245 (if (not was-modified)
1246 (set-buffer-modified-p nil)))
1247 (goto-char (cadr allout-after-save-decrypt))
1248 (setq allout-after-save-decrypt nil))
1249 )
19b84ba3
RS
1250
1251;;;_ #2 Mode activation
fd5359c6
MR
1252;;;_ = allout-explicitly-deactivated
1253(defvar allout-explicitly-deactivated nil
d82979ea 1254 "If t, `allout-mode's last deactivation was deliberate.
2265e017 1255So `allout-post-command-business' should not reactivate it...")
fd5359c6
MR
1256(make-variable-buffer-local 'allout-explicitly-deactivated)
1257;;;_ > allout-init (&optional mode)
1258(defun allout-init (&optional mode)
2265e017 1259 "Prime `allout-mode' to enable/disable auto-activation, wrt `allout-layout'.
19b84ba3 1260
8d118843 1261MODE is one of the following symbols:
19b84ba3 1262
353e2ef2
KH
1263 - nil \(or no argument) deactivate auto-activation/layout;
1264 - `activate', enable auto-activation only;
1265 - `ask', enable auto-activation, and enable auto-layout but with
a0776d6b 1266 confirmation for layout operation solicited from user each time;
353e2ef2 1267 - `report', just report and return the current auto-activation state;
8d118843
RS
1268 - anything else \(eg, t) for auto-activation and auto-layout, without
1269 any confirmation check.
19b84ba3 1270
e126900f 1271Use this function to setup your Emacs session for automatic activation
19b84ba3 1272of allout outline mode, contingent to the buffer-specific setting of
fd5359c6
MR
1273the `allout-layout' variable. (See `allout-layout' and
1274`allout-expose-topic' docstrings for more details on auto layout).
19b84ba3 1275
d82979ea
EZ
1276`allout-init' works by setting up (or removing) the `allout-mode'
1277find-file-hook, and giving `allout-auto-activation' a suitable
1278setting.
19b84ba3 1279
e126900f
JB
1280To prime your Emacs session for full auto-outline operation, include
1281the following two lines in your Emacs init file:
19b84ba3
RS
1282
1283\(require 'allout)
fd5359c6 1284\(allout-init t)"
19b84ba3 1285
d82979ea
EZ
1286 (interactive)
1287 (if (interactive-p)
1288 (progn
1289 (setq mode
1290 (completing-read
1291 (concat "Select outline auto setup mode "
1292 "(empty for report, ? for options) ")
1293 '(("nil")("full")("activate")("deactivate")
1294 ("ask") ("report") (""))
1295 nil
1296 t))
1297 (if (string= mode "")
1298 (setq mode 'report)
1299 (setq mode (intern-soft mode)))))
8d118843
RS
1300 (let
1301 ;; convenience aliases, for consistent ref to respective vars:
fd5359c6 1302 ((hook 'allout-find-file-hook)
0949617b
EZ
1303 (find-file-hook-var-name (if (boundp 'find-file-hook)
1304 'find-file-hook
1305 'find-file-hooks))
fd5359c6 1306 (curr-mode 'allout-auto-activation))
353e2ef2 1307
8d118843 1308 (cond ((not mode)
0949617b
EZ
1309 (set find-file-hook-var-name
1310 (delq hook (symbol-value find-file-hook-var-name)))
8d118843
RS
1311 (if (interactive-p)
1312 (message "Allout outline mode auto-activation inhibited.")))
1313 ((eq mode 'report)
0949617b 1314 (if (not (memq hook (symbol-value find-file-hook-var-name)))
d82979ea
EZ
1315 (allout-init nil)
1316 ;; Just punt and use the reports from each of the modes:
1317 (allout-init (symbol-value curr-mode))))
0949617b 1318 (t (add-hook find-file-hook-var-name hook)
353e2ef2 1319 (set curr-mode ; `set', not `setq'!
8d118843
RS
1320 (cond ((eq mode 'activate)
1321 (message
1322 "Outline mode auto-activation enabled.")
1323 'activate)
1324 ((eq mode 'report)
1325 ;; Return the current mode setting:
fd5359c6 1326 (allout-init mode))
8d118843
RS
1327 ((eq mode 'ask)
1328 (message
1329 (concat "Outline mode auto-activation and "
1330 "-layout \(upon confirmation) enabled."))
1331 'ask)
1332 ((message
1333 "Outline mode auto-activation and -layout enabled.")
1334 'full)))))))
fd5359c6
MR
1335;;;_ > allout-setup-menubar ()
1336(defun allout-setup-menubar ()
aad94676 1337 "Populate the current buffer's menubar with `allout-mode' stuff."
fd5359c6
MR
1338 (let ((menus (list allout-mode-exposure-menu
1339 allout-mode-editing-menu
1340 allout-mode-navigation-menu
1341 allout-mode-misc-menu))
9179616f
DL
1342 cur)
1343 (while menus
1344 (setq cur (car menus)
1345 menus (cdr menus))
1346 (easy-menu-add cur))))
01fc9422
EZ
1347;;;_ > allout-overlay-preparations
1348(defun allout-overlay-preparations ()
1349 "Set the properties of the allout invisible-text overlay and others."
1350 (setplist 'allout-exposure-category nil)
1351 (put 'allout-exposure-category 'invisible 'allout)
1352 (put 'allout-exposure-category 'evaporate t)
96b83743
EZ
1353 ;; XXX We use isearch-open-invisible *and* isearch-mode-end-hook. The
1354 ;; latter would be sufficient, but it seems that a separate behavior -
1355 ;; the _transient_ opening of invisible text during isearch - is keyed to
1356 ;; presence of the isearch-open-invisible property - even though this
1357 ;; property controls the isearch _arrival_ behavior. This is the case at
1358 ;; least in emacs 21, 22.0, and xemacs 21.4.
01fc9422 1359 (put 'allout-exposure-category 'isearch-open-invisible
96b83743
EZ
1360 'allout-isearch-end-handler)
1361 (if (featurep 'xemacs)
01fc9422
EZ
1362 (put 'allout-exposure-category 'start-open t)
1363 (put 'allout-exposure-category 'insert-in-front-hooks
96b83743 1364 '(allout-overlay-insert-in-front-handler)))
01fc9422
EZ
1365 (put 'allout-exposure-category 'modification-hooks
1366 '(allout-overlay-interior-modification-handler)))
fd5359c6 1367;;;_ > allout-mode (&optional toggle)
19b84ba3 1368;;;_ : Defun:
0949617b 1369;;;###autoload
fd5359c6 1370(defun allout-mode (&optional toggle)
c567ac01 1371;;;_ . Doc string:
19b84ba3 1372 "Toggle minor mode for controlling exposure and editing of text outlines.
96b83743 1373\\<allout-mode-map>
19b84ba3 1374
9179616f
DL
1375Optional arg forces mode to re-initialize iff arg is positive num or
1376symbol. Allout outline mode always runs as a minor mode.
19b84ba3 1377
aad94676 1378Allout outline mode provides extensive outline oriented formatting and
9179616f
DL
1379manipulation. It enables structural editing of outlines, as well as
1380navigation and exposure. It also is specifically aimed at
1381accommodating syntax-sensitive text like programming languages. \(For
1382an example, see the allout code itself, which is organized as an allout
1383outline.)
19b84ba3 1384
9179616f 1385In addition to outline navigation and exposure, allout includes:
19b84ba3 1386
d82979ea 1387 - topic-oriented repositioning, promotion/demotion, cut, and paste
9179616f
DL
1388 - integral outline exposure-layout
1389 - incremental search with dynamic exposure and reconcealment of hidden text
1390 - automatic topic-number maintenance
d82979ea 1391 - easy topic encryption and decryption
9179616f 1392 - \"Hot-spot\" operation, for single-keystroke maneuvering and
fd5359c6 1393 exposure control. \(See the allout-mode docstring.)
9179616f
DL
1394
1395and many other features.
19b84ba3
RS
1396
1397Below is a description of the bindings, and then explanation of
2265e017 1398special `allout-mode' features and terminology. See also the outline
9179616f 1399menubar additions for quick reference to many of the features, and see
f134d28b 1400the docstring of the function `allout-init' for instructions on
d82979ea 1401priming your emacs session for automatic activation of `allout-mode'.
9179616f 1402
19b84ba3 1403
fd5359c6
MR
1404The bindings are dictated by the `allout-keybindings-list' and
1405`allout-command-prefix' variables.
19b84ba3
RS
1406
1407 Navigation: Exposure Control:
1408 ---------- ----------------
0949617b
EZ
1409\\[allout-next-visible-heading] allout-next-visible-heading | \\[allout-hide-current-subtree] allout-hide-current-subtree
1410\\[allout-previous-visible-heading] allout-previous-visible-heading | \\[allout-show-children] allout-show-children
1411\\[allout-up-current-level] allout-up-current-level | \\[allout-show-current-subtree] allout-show-current-subtree
1412\\[allout-forward-current-level] allout-forward-current-level | \\[allout-show-current-entry] allout-show-current-entry
1413\\[allout-backward-current-level] allout-backward-current-level | \\[allout-show-all] allout-show-all
1414\\[allout-end-of-entry] allout-end-of-entry
96b83743 1415\\[allout-beginning-of-current-entry] allout-beginning-of-current-entry, alternately, goes to hot-spot
19b84ba3
RS
1416
1417 Topic Header Production:
1418 -----------------------
96b83743
EZ
1419\\[allout-open-sibtopic] allout-open-sibtopic Create a new sibling after current topic.
1420\\[allout-open-subtopic] allout-open-subtopic ... an offspring of current topic.
1421\\[allout-open-supertopic] allout-open-supertopic ... a sibling of the current topic's parent.
19b84ba3
RS
1422
1423 Topic Level and Prefix Adjustment:
1424 ---------------------------------
96b83743
EZ
1425\\[allout-shift-in] allout-shift-in Shift current topic and all offspring deeper.
1426\\[allout-shift-out] allout-shift-out ... less deep.
1427\\[allout-rebullet-current-heading] allout-rebullet-current-heading Prompt for alternate bullet for
0949617b
EZ
1428 current topic.
1429\\[allout-rebullet-topic] allout-rebullet-topic Reconcile bullets of topic and its offspring
19b84ba3
RS
1430 - distinctive bullets are not changed, others
1431 alternated according to nesting depth.
96b83743 1432\\[allout-number-siblings] allout-number-siblings Number bullets of topic and siblings - the
1977b8f6
RS
1433 offspring are not affected. With repeat
1434 count, revoke numbering.
1435
19b84ba3
RS
1436 Topic-oriented Killing and Yanking:
1437 ----------------------------------
96b83743
EZ
1438\\[allout-kill-topic] allout-kill-topic Kill current topic, including offspring.
1439\\[allout-kill-line] allout-kill-line Like kill-line, but reconciles numbering, etc.
1440\\[allout-yank] allout-yank Yank, adjusting depth of yanked topic to
1977b8f6 1441 depth of heading if yanking into bare topic
19b84ba3 1442 heading (ie, prefix sans text).
96b83743
EZ
1443\\[allout-yank-pop] allout-yank-pop Is to allout-yank as yank-pop is to yank
1444
1445 Topic-oriented Encryption:
1446 -------------------------
1447\\[allout-toggle-current-subtree-encryption] allout-toggle-current-subtree-encryption Encrypt/Decrypt topic content
1977b8f6 1448
19b84ba3
RS
1449 Misc commands:
1450 -------------
19b84ba3
RS
1451M-x outlineify-sticky Activate outline mode for current buffer,
1452 and establish a default file-var setting
fd5359c6 1453 for `allout-layout'.
96b83743 1454\\[allout-mark-topic] allout-mark-topic
0949617b 1455\\[allout-copy-exposed-to-buffer] allout-copy-exposed-to-buffer
9179616f 1456 Duplicate outline, sans concealed text, to
d82979ea
EZ
1457 buffer with name derived from derived from that
1458 of current buffer - \"*BUFFERNAME exposed*\".
96b83743 1459\\[allout-flatten-exposed-to-buffer] allout-flatten-exposed-to-buffer
9179616f
DL
1460 Like above 'copy-exposed', but convert topic
1461 prefixes to section.subsection... numeric
1462 format.
96b83743 1463\\[eval-expression] (allout-init t) Setup Emacs session for outline mode
19b84ba3 1464 auto-activation.
1977b8f6 1465
96b83743 1466 Topic Encryption
d82979ea 1467
96b83743
EZ
1468Outline mode supports gpg encryption of topics, with support for
1469symmetric and key-pair modes, passphrase timeout, passphrase
1470consistency checking, user-provided hinting for symmetric key
1471mode, and auto-encryption of topics pending encryption on save.
1472\(Topics pending encryption are, by default, automatically
1473encrypted during file saves; if you're editing the contents of
1474such a topic, it is automatically decrypted for continued
1475editing.) The aim is reliable topic privacy while preventing
1476accidents like neglected encryption before saves, forgetting
1477which passphrase was used, and other practical pitfalls.
d82979ea 1478
69027dea 1479See `allout-toggle-current-subtree-encryption' function docstring and
d82979ea
EZ
1480`allout-encrypt-unencrypted-on-saves' customization variable for details.
1481
19b84ba3 1482 HOT-SPOT Operation
c567ac01 1483
19b84ba3
RS
1484Hot-spot operation provides a means for easy, single-keystroke outline
1485navigation and exposure control.
c567ac01 1486
19b84ba3
RS
1487When the text cursor is positioned directly on the bullet character of
1488a topic, regular characters (a to z) invoke the commands of the
fd5359c6 1489corresponding allout-mode keymap control chars. For example, \"f\"
96b83743 1490would invoke the command typically bound to \"C-c<space>C-f\"
fd5359c6 1491\(\\[allout-forward-current-level] `allout-forward-current-level').
19b84ba3 1492
96b83743
EZ
1493Thus, by positioning the cursor on a topic bullet, you can
1494execute the outline navigation and manipulation commands with a
1495single keystroke. Regular navigation keys (eg, \\[forward-char], \\[next-line]) never get
1496this special translation, so you can use them to get out of the
1497hot-spot and back to normal operation.
c567ac01 1498
fd5359c6 1499Note that the command `allout-beginning-of-current-entry' \(\\[allout-beginning-of-current-entry]\)
c567ac01 1500will move to the hot-spot when the cursor is already located at the
96b83743 1501beginning of the current entry, so you usually can hit \\[allout-beginning-of-current-entry]
c567ac01 1502twice in a row to get to the hot-spot.
1977b8f6 1503
19b84ba3 1504 Terminology
1977b8f6 1505
c567ac01
RS
1506Topic hierarchy constituents - TOPICS and SUBTOPICS:
1507
e126900f 1508TOPIC: A basic, coherent component of an Emacs outline. It can
96b83743 1509 contain and be contained by other topics.
c567ac01
RS
1510CURRENT topic:
1511 The visible topic most immediately containing the cursor.
19b84ba3
RS
1512DEPTH: The degree of nesting of a topic; it increases with
1513 containment. Also called the:
c567ac01
RS
1514LEVEL: The same as DEPTH.
1515
1516ANCESTORS:
1517 The topics that contain a topic.
1518PARENT: A topic's immediate ancestor. It has a depth one less than
1519 the topic.
1520OFFSPRING:
19b84ba3
RS
1521 The topics contained by a topic;
1522SUBTOPIC:
1523 An immediate offspring of a topic;
c567ac01
RS
1524CHILDREN:
1525 The immediate offspring of a topic.
1526SIBLINGS:
19b84ba3 1527 Topics having the same parent and depth.
353e2ef2 1528
c567ac01
RS
1529Topic text constituents:
1530
1531HEADER: The first line of a topic, include the topic PREFIX and header
353e2ef2 1532 text.
d82979ea
EZ
1533PREFIX: The leading text of a topic which distinguishes it from normal
1534 text. It has a strict form, which consists of a prefix-lead
1535 string, padding, and a bullet. The bullet may be followed by a
1536 number, indicating the ordinal number of the topic among its
1537 siblings, a space, and then the header text.
c567ac01
RS
1538
1539 The relative length of the PREFIX determines the nesting depth
1540 of the topic.
1541PREFIX-LEAD:
353e2ef2 1542 The string at the beginning of a topic prefix, normally a `.'.
c567ac01 1543 It can be customized by changing the setting of
2265e017 1544 `allout-header-prefix' and then reinitializing `allout-mode'.
c567ac01
RS
1545
1546 By setting the prefix-lead to the comment-string of a
aad94676 1547 programming language, you can embed outline structuring in
c567ac01 1548 program code without interfering with the language processing
fd5359c6 1549 of that code. See `allout-use-mode-specific-leader'
19b84ba3 1550 docstring for more detail.
c567ac01
RS
1551PREFIX-PADDING:
1552 Spaces or asterisks which separate the prefix-lead and the
96b83743 1553 bullet, determining the depth of the topic.
c567ac01 1554BULLET: A character at the end of the topic prefix, it must be one of
fd5359c6
MR
1555 the characters listed on `allout-plain-bullets-string' or
1556 `allout-distinctive-bullets-string'. (See the documentation
c567ac01 1557 for these variables for more details.) The default choice of
96b83743
EZ
1558 bullet when generating topics varies in a cycle with the depth of
1559 the topic.
c567ac01
RS
1560ENTRY: The text contained in a topic before any offspring.
1561BODY: Same as ENTRY.
1562
1563
1564EXPOSURE:
1565 The state of a topic which determines the on-screen visibility
353e2ef2 1566 of its offspring and contained text.
c567ac01
RS
1567CONCEALED:
1568 Topics and entry text whose display is inhibited. Contiguous
353e2ef2 1569 units of concealed text is represented by `...' ellipses.
c567ac01
RS
1570
1571 Concealed topics are effectively collapsed within an ancestor.
1572CLOSED: A topic whose immediate offspring and body-text is concealed.
353e2ef2 1573OPEN: A topic that is not closed, though its offspring or body may be."
c567ac01 1574;;;_ . Code
1977b8f6
RS
1575 (interactive "P")
1576
c567ac01 1577 (let* ((active (and (not (equal major-mode 'outline))
fd5359c6 1578 (allout-mode-p)))
353e2ef2 1579 ; Massage universal-arg `toggle' val:
c567ac01 1580 (toggle (and toggle
19b84ba3
RS
1581 (or (and (listp toggle)(car toggle))
1582 toggle)))
a0776d6b 1583 ; Activation specifically demanded?
69027dea 1584 (explicit-activation (and toggle
19b84ba3 1585 (or (symbolp toggle)
69027dea
EZ
1586 (and (wholenump toggle)
1587 (not (zerop toggle))))))
fd5359c6
MR
1588 ;; allout-mode already called once during this complex command?
1589 (same-complex-command (eq allout-v18/19-file-var-hack
19b84ba3 1590 (car command-history)))
96b83743
EZ
1591 (write-file-hook-var-name (cond ((boundp 'write-file-functions)
1592 'write-file-functions)
1593 ((boundp 'write-file-hooks)
1594 'write-file-hooks)
1595 (t 'local-write-file-hooks)))
19b84ba3
RS
1596 do-layout
1597 )
c567ac01 1598
19b84ba3 1599 ; See comments below re v19.18,.19 bug.
fd5359c6 1600 (setq allout-v18/19-file-var-hack (car command-history))
c567ac01 1601
1977b8f6
RS
1602 (cond
1603
19b84ba3 1604 ;; Provision for v19.18, 19.19 bug -
c567ac01
RS
1605 ;; Emacs v 19.18, 19.19 file-var code invokes prop-line-designated
1606 ;; modes twice when file is visited. We have to avoid toggling mode
1607 ;; off on second invocation, so we detect it as best we can, and
1608 ;; skip everything.
1609 ((and same-complex-command ; Still in same complex command
d82979ea 1610 ; as last time `allout-mode' invoked.
19b84ba3
RS
1611 active ; Already activated.
1612 (not explicit-activation) ; Prop-line file-vars don't have args.
1613 (string-match "^19.1[89]" ; Bug only known to be in v19.18 and
1614 emacs-version)); 19.19.
c567ac01 1615 t)
353e2ef2 1616
19b84ba3 1617 ;; Deactivation:
c567ac01 1618 ((and (not explicit-activation)
19b84ba3
RS
1619 (or active toggle))
1620 ; Activation not explicitly
1621 ; requested, and either in
1622 ; active state or *de*activation
1623 ; specifically requested:
fd5359c6 1624 (setq allout-explicitly-deactivated t)
c567ac01 1625
01fc9422
EZ
1626 (allout-do-resumptions)
1627
96b83743 1628 (remove-from-invisibility-spec '(allout . t))
01fc9422
EZ
1629 (remove-hook 'pre-command-hook 'allout-pre-command-business t)
1630 (remove-hook 'post-command-hook 'allout-post-command-business t)
1631 (when (featurep 'xemacs)
1632 (remove-hook 'before-change-functions 'allout-before-change-handler t))
1633 (remove-hook 'isearch-mode-end-hook 'allout-isearch-end-handler t)
1634 (remove-hook write-file-hook-var-name 'allout-write-file-hook-handler t)
1635 (remove-hook 'auto-save-hook 'allout-auto-save-hook-handler t)
1636
1637 (remove-overlays (point-min) (point-max)
1638 'category 'allout-exposure-category)
1639
1640 (run-hooks 'allout-mode-deactivate-hook)
fd5359c6 1641 (setq allout-mode nil))
1977b8f6 1642
19b84ba3 1643 ;; Activation:
1977b8f6 1644 ((not active)
fd5359c6
MR
1645 (setq allout-explicitly-deactivated nil)
1646 (if allout-old-style-prefixes
01fc9422
EZ
1647 ;; Inhibit all the fancy formatting:
1648 (allout-add-resumptions '((allout-primary-bullet "*")
1649 (allout-old-style-prefixes ()))))
19b84ba3 1650
01fc9422 1651 (allout-overlay-preparations) ; Doesn't hurt to redo this.
96b83743 1652
fd5359c6
MR
1653 (allout-infer-header-lead)
1654 (allout-infer-body-reindent)
19b84ba3 1655
fd5359c6 1656 (set-allout-regexp)
19b84ba3 1657
01fc9422
EZ
1658 ;; Produce map from current version of allout-keybindings-list:
1659 (setq allout-mode-map
1660 (produce-allout-mode-map allout-keybindings-list))
1661 (substitute-key-definition 'beginning-of-line
1662 'move-beginning-of-line
1663 allout-mode-map global-map)
1664 (substitute-key-definition 'end-of-line
1665 'move-end-of-line
1666 allout-mode-map global-map)
1667 (produce-allout-mode-menubar-entries)
1668 (fset 'allout-mode-map allout-mode-map)
1669
1670 ;; Include on minor-mode-map-alist, if not already there:
1671 (if (not (member '(allout-mode . allout-mode-map)
1672 minor-mode-map-alist))
1673 (setq minor-mode-map-alist
1674 (cons '(allout-mode . allout-mode-map)
1675 minor-mode-map-alist)))
353e2ef2 1676
96b83743 1677 (add-to-invisibility-spec '(allout . t))
01fc9422
EZ
1678 (allout-add-resumptions '(line-move-ignore-invisible t))
1679 (add-hook 'pre-command-hook 'allout-pre-command-business nil t)
1680 (add-hook 'post-command-hook 'allout-post-command-business nil t)
1681 (when (featurep 'xemacs)
1682 (add-hook 'before-change-functions 'allout-before-change-handler
1683 nil t))
1684 (add-hook 'isearch-mode-end-hook 'allout-isearch-end-handler nil t)
1685 (add-hook write-file-hook-var-name 'allout-write-file-hook-handler
1686 nil t)
1687 (add-hook 'auto-save-hook 'allout-auto-save-hook-handler
1688 nil t)
1689
1690 ;; Stash auto-fill settings and adjust so custom allout auto-fill
1691 ;; func will be used if auto-fill is active or activated. (The
1692 ;; custom func respects topic headline, maintains hanging-indents,
1693 ;; etc.)
1694 (if (and auto-fill-function (not allout-inhibit-auto-fill))
1695 ;; allout-auto-fill will use the stashed values and so forth.
1696 (allout-add-resumptions '(auto-fill-function allout-auto-fill)))
1697 (allout-add-resumptions (list 'allout-former-auto-filler
1698 auto-fill-function)
1699 ;; Register allout-auto-fill to be used if
1700 ;; filling is active:
1701 (list 'allout-outside-normal-auto-fill-function
1702 normal-auto-fill-function)
1703 '(normal-auto-fill-function allout-auto-fill)
1704 ;; Paragraphs are broken by topic headlines.
1705 (list 'paragraph-start
1706 (concat paragraph-start "\\|^\\("
1707 allout-regexp "\\)"))
1708 (list 'paragraph-separate
1709 (concat paragraph-separate "\\|^\\("
1710 allout-regexp "\\)")))
fd5359c6 1711 (or (assq 'allout-mode minor-mode-alist)
c567ac01 1712 (setq minor-mode-alist
aad94676 1713 (cons '(allout-mode " Allout") minor-mode-alist)))
19b84ba3 1714
fd5359c6 1715 (allout-setup-menubar)
9179616f 1716
fd5359c6 1717 (if allout-layout
19b84ba3 1718 (setq do-layout t))
1977b8f6 1719
fd5359c6
MR
1720 (run-hooks 'allout-mode-hook)
1721 (setq allout-mode t))
19b84ba3
RS
1722
1723 ;; Reactivation:
8d118843 1724 ((setq do-layout t)
fd5359c6 1725 (allout-infer-body-reindent))
01fc9422 1726 ) ;; end of activation-mode cases.
19b84ba3 1727
01fc9422 1728 ;; Do auto layout if warranted:
4034b0e2
LT
1729 (let ((use-layout (if (listp allout-layout)
1730 allout-layout
1731 allout-default-layout)))
1732 (if (and do-layout
1733 allout-auto-activation
1734 use-layout
1735 (and (not (eq allout-auto-activation 'activate))
1736 (if (eq allout-auto-activation 'ask)
1737 (if (y-or-n-p (format "Expose %s with layout '%s'? "
1738 (buffer-name)
1739 use-layout))
1740 t
1741 (message "Skipped %s layout." (buffer-name))
1742 nil)
1743 t)))
1744 (save-excursion
1745 (message "Adjusting '%s' exposure..." (buffer-name))
1746 (goto-char 0)
1747 (allout-this-or-next-heading)
1748 (condition-case err
1749 (progn
1750 (apply 'allout-expose-topic (list use-layout))
1751 (message "Adjusting '%s' exposure... done." (buffer-name)))
1752 ;; Problem applying exposure - notify user, but don't
1753 ;; interrupt, eg, file visit:
1754 (error (message "%s" (car (cdr err)))
1755 (sit-for 1))))))
fd5359c6 1756 allout-mode
c567ac01 1757 ) ; let*
19b84ba3 1758 ) ; defun
46771bb3
EZ
1759;;;_ > allout-minor-mode
1760(defalias 'allout-minor-mode 'allout-mode)
773e7e48
EZ
1761
1762;;;_ - Position Assessment
1763;;;_ > allout-hidden-p (&optional pos)
1764(defsubst allout-hidden-p (&optional pos)
1765 "Non-nil if the character after point is invisible."
da506f05 1766 (eq (get-char-property (or pos (point)) 'invisible) 'allout))
773e7e48 1767
96b83743
EZ
1768;;;_ > allout-overlay-insert-in-front-handler (ol after beg end
1769;;; &optional prelen)
1770(defun allout-overlay-insert-in-front-handler (ol after beg end
1771 &optional prelen)
1772 "Shift the overlay so stuff inserted in front of it are excluded."
1773 (if after
1774 (move-overlay ol (1+ beg) (overlay-end ol))))
1775;;;_ > allout-overlay-interior-modification-handler (ol after beg end
1776;;; &optional prelen)
1777(defun allout-overlay-interior-modification-handler (ol after beg end
1778 &optional prelen)
1779 "Get confirmation before making arbitrary changes to invisible text.
1780
1781We expose the invisible text and ask for confirmation. Refusal or
1782keyboard-quit abandons the changes, with keyboard-quit additionally
1783reclosing the opened text.
1784
1785No confirmation is necessary when inhibit-read-only is set - eg, allout
1786internal functions use this feature cohesively bunch changes."
1787
1788 (when (and (not inhibit-read-only) (not after))
1789 (let ((start (point))
1790 (ol-start (overlay-start ol))
1791 (ol-end (overlay-end ol))
96b83743
EZ
1792 first)
1793 (goto-char beg)
1794 (while (< (point) end)
1795 (when (allout-hidden-p)
1796 (allout-show-to-offshoot)
1797 (if (allout-hidden-p)
1798 (save-excursion (forward-char 1)
1799 (allout-show-to-offshoot)))
1800 (when (not first)
96b83743
EZ
1801 (setq first (point))))
1802 (goto-char (if (featurep 'xemacs)
1803 (next-property-change (1+ (point)) nil end)
1804 (next-char-property-change (1+ (point)) end))))
1805 (when first
1806 (goto-char first)
1807 (condition-case nil
1808 (if (not
1809 (yes-or-no-p
1810 (substitute-command-keys
4034b0e2 1811 (concat "Modify concealed text? (\"no\" just aborts,"
96b83743
EZ
1812 " \\[keyboard-quit] also reconceals) "))))
1813 (progn (goto-char start)
1814 (error "Concealed-text change refused.")))
1815 (quit (allout-flag-region ol-start ol-end nil)
1816 (allout-flag-region ol-start ol-end t)
1817 (error "Concealed-text change abandoned, text reconcealed."))))
1818 (goto-char start))))
1819;;;_ > allout-before-change-handler (beg end)
1820(defun allout-before-change-handler (beg end)
1821 "Protect against changes to invisible text.
1822
1823See allout-overlay-interior-modification-handler for details.
1824
1825This before-change handler is used only where modification-hooks
1826overlay property is not supported."
01fc9422
EZ
1827 ;; allout-overlay-interior-modification-handler on an overlay handles
1828 ;; this in other emacs, via `allout-exposure-category's 'modification-hooks.
1829 (when (and (featurep 'xemacs) (allout-mode-p))
1830 ;; process all of the pending overlays:
1831 (dolist (overlay (overlays-in beg end))
1832 (if (eq (overlay-get ol 'invisible) 'allout)
1833 (allout-overlay-interior-modification-handler
1834 overlay nil beg end nil)))))
96b83743
EZ
1835;;;_ > allout-isearch-end-handler (&optional overlay)
1836(defun allout-isearch-end-handler (&optional overlay)
1837 "Reconcile allout outline exposure on arriving in hidden text after isearch.
1838
1839Optional OVERLAY parameter is for when this function is used by
1840`isearch-open-invisible' overlay property. It is otherwise unused, so this
1841function can also be used as an `isearch-mode-end-hook'."
1842
1843 (if (and (allout-mode-p) (allout-hidden-p))
1844 (allout-show-to-offshoot)))
1845
fd5359c6 1846;;;_ #3 Internal Position State-Tracking - "allout-recent-*" funcs
19b84ba3 1847;;; All the basic outline functions that directly do string matches to
1977b8f6 1848;;; evaluate heading prefix location set the variables
fd5359c6
MR
1849;;; `allout-recent-prefix-beginning' and `allout-recent-prefix-end'
1850;;; when successful. Functions starting with `allout-recent-' all
19b84ba3
RS
1851;;; use this state, providing the means to avoid redundant searches
1852;;; for just-established data. This optimization can provide
1853;;; significant speed improvement, but it must be employed carefully.
fd5359c6
MR
1854;;;_ = allout-recent-prefix-beginning
1855(defvar allout-recent-prefix-beginning 0
c567ac01 1856 "Buffer point of the start of the last topic prefix encountered.")
fd5359c6
MR
1857(make-variable-buffer-local 'allout-recent-prefix-beginning)
1858;;;_ = allout-recent-prefix-end
1859(defvar allout-recent-prefix-end 0
c567ac01 1860 "Buffer point of the end of the last topic prefix encountered.")
fd5359c6
MR
1861(make-variable-buffer-local 'allout-recent-prefix-end)
1862;;;_ = allout-recent-end-of-subtree
1863(defvar allout-recent-end-of-subtree 0
2265e017 1864 "Buffer point last returned by `allout-end-of-current-subtree'.")
fd5359c6
MR
1865(make-variable-buffer-local 'allout-recent-end-of-subtree)
1866;;;_ > allout-prefix-data (beg end)
d82979ea 1867(defmacro allout-prefix-data (beg end)
fd5359c6
MR
1868 "Register allout-prefix state data - BEGINNING and END of prefix.
1869
1870For reference by `allout-recent' funcs. Returns BEGINNING."
1871 `(setq allout-recent-prefix-end ,end
d82979ea 1872 allout-recent-prefix-beginning ,beg))
fd5359c6
MR
1873;;;_ > allout-recent-depth ()
1874(defmacro allout-recent-depth ()
19b84ba3 1875 "Return depth of last heading encountered by an outline maneuvering function.
c567ac01
RS
1876
1877All outline functions which directly do string matches to assess
2265e017
MR
1878headings set the variables `allout-recent-prefix-beginning' and
1879`allout-recent-prefix-end' if successful. This function uses those settings
c567ac01
RS
1880to return the current depth."
1881
fd5359c6
MR
1882 '(max 1 (- allout-recent-prefix-end
1883 allout-recent-prefix-beginning
1884 allout-header-subtraction)))
1885;;;_ > allout-recent-prefix ()
1886(defmacro allout-recent-prefix ()
2265e017 1887 "Like `allout-recent-depth', but returns text of last encountered prefix.
c567ac01
RS
1888
1889All outline functions which directly do string matches to assess
2265e017
MR
1890headings set the variables `allout-recent-prefix-beginning' and
1891`allout-recent-prefix-end' if successful. This function uses those settings
c567ac01 1892to return the current depth."
fd5359c6
MR
1893 '(buffer-substring allout-recent-prefix-beginning
1894 allout-recent-prefix-end))
1895;;;_ > allout-recent-bullet ()
1896(defmacro allout-recent-bullet ()
1897 "Like allout-recent-prefix, but returns bullet of last encountered prefix.
c567ac01
RS
1898
1899All outline functions which directly do string matches to assess
2265e017
MR
1900headings set the variables `allout-recent-prefix-beginning' and
1901`allout-recent-prefix-end' if successful. This function uses those settings
c567ac01 1902to return the current depth of the most recently matched topic."
fd5359c6
MR
1903 '(buffer-substring (1- allout-recent-prefix-end)
1904 allout-recent-prefix-end))
c567ac01 1905
19b84ba3 1906;;;_ #4 Navigation
c567ac01 1907
46771bb3 1908;;;_ - Position Assessment
c567ac01 1909;;;_ : Location Predicates
fd5359c6
MR
1910;;;_ > allout-on-current-heading-p ()
1911(defun allout-on-current-heading-p ()
19b84ba3
RS
1912 "Return non-nil if point is on current visible topics' header line.
1913
1914Actually, returns prefix beginning point."
1977b8f6 1915 (save-excursion
96b83743 1916 (allout-beginning-of-current-line)
fd5359c6
MR
1917 (and (looking-at allout-regexp)
1918 (allout-prefix-data (match-beginning 0) (match-end 0)))))
1919;;;_ > allout-on-heading-p ()
1920(defalias 'allout-on-heading-p 'allout-on-current-heading-p)
1921;;;_ > allout-e-o-prefix-p ()
1922(defun allout-e-o-prefix-p ()
19b84ba3 1923 "True if point is located where current topic prefix ends, heading begins."
0458fa80
EZ
1924 (and (save-excursion (let ((inhibit-field-text-motion t))
1925 (beginning-of-line))
fd5359c6
MR
1926 (looking-at allout-regexp))
1927 (= (point)(save-excursion (allout-end-of-prefix)(point)))))
c567ac01 1928;;;_ : Location attributes
fd5359c6 1929;;;_ > allout-depth ()
96b83743
EZ
1930(defun allout-depth ()
1931 "Return depth of topic most immediately containing point.
1932
1933Return zero if point is not within any topic.
1934
1935Like `allout-current-depth', but respects hidden as well as visible topics."
9179616f 1936 (save-excursion
96b83743
EZ
1937 (let ((start-point (point)))
1938 (if (and (allout-goto-prefix)
1939 (not (< start-point (point))))
1940 (allout-recent-depth)
1941 (progn
1942 ;; Oops, no prefix, zero prefix data:
1943 (allout-prefix-data (point)(point))
1944 ;; ... and return 0:
1945 0)))))
fd5359c6 1946;;;_ > allout-current-depth ()
96b83743
EZ
1947(defun allout-current-depth ()
1948 "Return depth of visible topic most immediately containing point.
1949
1950Return zero if point is not within any topic."
1951 (save-excursion
1952 (if (allout-back-to-current-heading)
1953 (max 1
1954 (- allout-recent-prefix-end
1955 allout-recent-prefix-beginning
1956 allout-header-subtraction))
1957 0)))
fd5359c6
MR
1958;;;_ > allout-get-current-prefix ()
1959(defun allout-get-current-prefix ()
c567ac01 1960 "Topic prefix of the current topic."
1977b8f6 1961 (save-excursion
fd5359c6
MR
1962 (if (allout-goto-prefix)
1963 (allout-recent-prefix))))
1964;;;_ > allout-get-bullet ()
1965(defun allout-get-bullet ()
c567ac01 1966 "Return bullet of containing topic (visible or not)."
1977b8f6 1967 (save-excursion
fd5359c6
MR
1968 (and (allout-goto-prefix)
1969 (allout-recent-bullet))))
1970;;;_ > allout-current-bullet ()
1971(defun allout-current-bullet ()
c567ac01 1972 "Return bullet of current (visible) topic heading, or none if none found."
96b83743 1973 (condition-case nil
1977b8f6 1974 (save-excursion
fd5359c6
MR
1975 (allout-back-to-current-heading)
1976 (buffer-substring (- allout-recent-prefix-end 1)
1977 allout-recent-prefix-end))
1977b8f6 1978 ;; Quick and dirty provision, ostensibly for missing bullet:
9179616f 1979 ('args-out-of-range nil))
1977b8f6 1980 )
fd5359c6
MR
1981;;;_ > allout-get-prefix-bullet (prefix)
1982(defun allout-get-prefix-bullet (prefix)
c567ac01 1983 "Return the bullet of the header prefix string PREFIX."
1977b8f6
RS
1984 ;; Doesn't make sense if we're old-style prefixes, but this just
1985 ;; oughtn't be called then, so forget about it...
fd5359c6 1986 (if (string-match allout-regexp prefix)
1977b8f6 1987 (substring prefix (1- (match-end 0)) (match-end 0))))
fd5359c6
MR
1988;;;_ > allout-sibling-index (&optional depth)
1989(defun allout-sibling-index (&optional depth)
9179616f
DL
1990 "Item number of this prospective topic among its siblings.
1991
e126900f 1992If optional arg DEPTH is greater than current depth, then we're
9179616f
DL
1993opening a new level, and return 0.
1994
1995If less than this depth, ascend to that depth and count..."
1996
1997 (save-excursion
1998 (cond ((and depth (<= depth 0) 0))
fd5359c6 1999 ((or (not depth) (= depth (allout-depth)))
9179616f 2000 (let ((index 1))
fd5359c6 2001 (while (allout-previous-sibling (allout-recent-depth) nil)
9179616f
DL
2002 (setq index (1+ index)))
2003 index))
fd5359c6
MR
2004 ((< depth (allout-recent-depth))
2005 (allout-ascend-to-depth depth)
2006 (allout-sibling-index))
9179616f 2007 (0))))
fd5359c6
MR
2008;;;_ > allout-topic-flat-index ()
2009(defun allout-topic-flat-index ()
9179616f
DL
2010 "Return a list indicating point's numeric section.subsect.subsubsect...
2011Outermost is first."
fd5359c6
MR
2012 (let* ((depth (allout-depth))
2013 (next-index (allout-sibling-index depth))
9179616f
DL
2014 (rev-sibls nil))
2015 (while (> next-index 0)
2016 (setq rev-sibls (cons next-index rev-sibls))
2017 (setq depth (1- depth))
fd5359c6 2018 (setq next-index (allout-sibling-index depth)))
9179616f
DL
2019 rev-sibls)
2020 )
1977b8f6 2021
96b83743
EZ
2022;;;_ - Navigation routines
2023;;;_ > allout-beginning-of-current-line ()
2024(defun allout-beginning-of-current-line ()
2025 "Like beginning of line, but to visible text."
2026
5204800f
NR
2027 ;; This combination of move-beginning-of-line and beginning-of-line is
2028 ;; deliberate, but the (beginning-of-line) may now be superfluous.
0458fa80
EZ
2029 (let ((inhibit-field-text-motion t))
2030 (move-beginning-of-line 1)
96b83743 2031 (beginning-of-line)
0458fa80
EZ
2032 (while (and (not (bobp)) (or (not (bolp)) (allout-hidden-p)))
2033 (beginning-of-line)
2034 (if (or (allout-hidden-p) (not (bolp)))
2035 (forward-char -1)))))
96b83743
EZ
2036;;;_ > allout-end-of-current-line ()
2037(defun allout-end-of-current-line ()
2038 "Move to the end of line, past concealed text if any."
2039 ;; XXX This is for symmetry with `allout-beginning-of-current-line' -
2040 ;; `move-end-of-line' doesn't suffer the same problem as
2041 ;; `move-beginning-of-line'.
0458fa80 2042 (let ((inhibit-field-text-motion t))
96b83743 2043 (end-of-line)
0458fa80
EZ
2044 (while (allout-hidden-p)
2045 (end-of-line)
2046 (if (allout-hidden-p) (forward-char 1)))))
fd5359c6
MR
2047;;;_ > allout-next-heading ()
2048(defsubst allout-next-heading ()
01fc9422 2049 "Move to the heading for the topic \(possibly invisible) after this one.
c567ac01
RS
2050
2051Returns the location of the heading, or nil if none found."
2052
01fc9422
EZ
2053 (if (and (bobp) (not (eobp)) (looking-at allout-regexp))
2054 (forward-char 1))
c567ac01 2055
fd5359c6
MR
2056 (if (re-search-forward allout-line-boundary-regexp nil 0)
2057 (allout-prefix-data ; Got valid location state - set vars:
9179616f 2058 (goto-char (or (match-beginning 2)
fd5359c6
MR
2059 allout-recent-prefix-beginning))
2060 (or (match-end 2) allout-recent-prefix-end))))
96b83743 2061;;;_ > allout-this-or-next-heading
fd5359c6 2062(defun allout-this-or-next-heading ()
8d118843 2063 "Position cursor on current or next heading."
fd5359c6
MR
2064 ;; A throwaway non-macro that is defined after allout-next-heading
2065 ;; and usable by allout-mode.
2066 (if (not (allout-goto-prefix)) (allout-next-heading)))
2067;;;_ > allout-previous-heading ()
2068(defmacro allout-previous-heading ()
19b84ba3 2069 "Move to the prior \(possibly invisible) heading line.
c567ac01
RS
2070
2071Return the location of the beginning of the heading, or nil if not found."
2072
2073 '(if (bobp)
2074 nil
fd5359c6 2075 (allout-goto-prefix)
c567ac01
RS
2076 (if
2077 ;; searches are unbounded and return nil if failed:
fd5359c6
MR
2078 (or (re-search-backward allout-line-boundary-regexp nil 0)
2079 (looking-at allout-bob-regexp))
19b84ba3 2080 (progn ; Got valid location state - set vars:
fd5359c6 2081 (allout-prefix-data
c567ac01 2082 (goto-char (or (match-beginning 2)
fd5359c6
MR
2083 allout-recent-prefix-beginning))
2084 (or (match-end 2) allout-recent-prefix-end))))))
96b83743
EZ
2085;;;_ > allout-get-invisibility-overlay ()
2086(defun allout-get-invisibility-overlay ()
2087 "Return the overlay at point that dictates allout invisibility."
2088 (let ((overlays (overlays-at (point)))
2089 got)
2090 (while (and overlays (not got))
2091 (if (equal (overlay-get (car overlays) 'invisible) 'allout)
2092 (setq got (car overlays))))
2093 got))
2094;;;_ > allout-back-to-visible-text ()
2095(defun allout-back-to-visible-text ()
2096 "Move to most recent prior character that is visible, and return point."
2097 (if (allout-hidden-p)
2098 (goto-char (overlay-start (allout-get-invisibility-overlay))))
2099 (point))
c567ac01
RS
2100
2101;;;_ - Subtree Charting
2102;;;_ " These routines either produce or assess charts, which are
2103;;; nested lists of the locations of topics within a subtree.
2104;;;
2105;;; Use of charts enables efficient navigation of subtrees, by
2106;;; requiring only a single regexp-search based traversal, to scope
2107;;; out the subtopic locations. The chart then serves as the basis
9179616f
DL
2108;;; for assessment or adjustment of the subtree, without redundant
2109;;; traversal of the structure.
c567ac01 2110
fd5359c6
MR
2111;;;_ > allout-chart-subtree (&optional levels orig-depth prev-depth)
2112(defun allout-chart-subtree (&optional levels orig-depth prev-depth)
19b84ba3
RS
2113 "Produce a location \"chart\" of subtopics of the containing topic.
2114
a0776d6b 2115Optional argument LEVELS specifies the depth \(relative to start
d82979ea
EZ
2116depth) for the chart. Subsequent optional args are not for public
2117use.
2118
2119Point is left at the end of the subtree.
19b84ba3 2120
d82979ea 2121Charts are used to capture outline structure, so that outline-altering
19b84ba3
RS
2122routines need assess the structure only once, and then use the chart
2123for their elaborate manipulations.
2124
2125Topics are entered in the chart so the last one is at the car.
2126The entry for each topic consists of an integer indicating the point
2127at the beginning of the topic. Charts for offspring consists of a
2128list containing, recursively, the charts for the respective subtopics.
2129The chart for a topics' offspring precedes the entry for the topic
2130itself.
2131
d82979ea
EZ
2132The other function parameters are for internal recursion, and should
2133not be specified by external callers. ORIG-DEPTH is depth of topic at
2134starting point, and PREV-DEPTH is depth of prior topic."
19b84ba3 2135
353e2ef2 2136 (let ((original (not orig-depth)) ; `orig-depth' set only in recursion.
19b84ba3
RS
2137 chart curr-depth)
2138
2139 (if original ; Just starting?
2140 ; Register initial settings and
2141 ; position to first offspring:
fd5359c6 2142 (progn (setq orig-depth (allout-depth))
19b84ba3 2143 (or prev-depth (setq prev-depth (1+ orig-depth)))
fd5359c6 2144 (allout-next-heading)))
c567ac01 2145
19b84ba3
RS
2146 ;; Loop over the current levels' siblings. Besides being more
2147 ;; efficient than tail-recursing over a level, it avoids exceeding
e126900f 2148 ;; the typically quite constrained Emacs max-lisp-eval-depth.
9179616f 2149 ;;
19b84ba3
RS
2150 ;; Probably would speed things up to implement loop-based stack
2151 ;; operation rather than recursing for lower levels. Bah.
9179616f 2152
c567ac01 2153 (while (and (not (eobp))
19b84ba3 2154 ; Still within original topic?
fd5359c6 2155 (< orig-depth (setq curr-depth (allout-recent-depth)))
19b84ba3
RS
2156 (cond ((= prev-depth curr-depth)
2157 ;; Register this one and move on:
2158 (setq chart (cons (point) chart))
2159 (if (and levels (<= levels 1))
2160 ;; At depth limit - skip sublevels:
fd5359c6 2161 (or (allout-next-sibling curr-depth)
19b84ba3
RS
2162 ;; or no more siblings - proceed to
2163 ;; next heading at lesser depth:
e6a9aec7 2164 (while (and (<= curr-depth
fd5359c6
MR
2165 (allout-recent-depth))
2166 (allout-next-heading))))
2167 (allout-next-heading)))
19b84ba3
RS
2168
2169 ((and (< prev-depth curr-depth)
2170 (or (not levels)
2171 (> levels 0)))
2172 ;; Recurse on deeper level of curr topic:
2173 (setq chart
fd5359c6 2174 (cons (allout-chart-subtree (and levels
19b84ba3
RS
2175 (1- levels))
2176 orig-depth
2177 curr-depth)
2178 chart))
2179 ;; ... then continue with this one.
2180 )
2181
2182 ;; ... else nil if we've ascended back to prev-depth.
2183
2184 )))
2185
2186 (if original ; We're at the last sibling on
2187 ; the original level. Position
2188 ; to the end of it:
c567ac01 2189 (progn (and (not (eobp)) (forward-char -1))
96b83743
EZ
2190 (and (= (preceding-char) ?\n)
2191 (= (aref (buffer-substring (max 1 (- (point) 3))
2192 (point))
2193 1)
2194 ?\n)
19b84ba3 2195 (forward-char -1))
fd5359c6 2196 (setq allout-recent-end-of-subtree (point))))
353e2ef2 2197
c567ac01
RS
2198 chart ; (nreverse chart) not necessary,
2199 ; and maybe not preferable.
2200 ))
fd5359c6
MR
2201;;;_ > allout-chart-siblings (&optional start end)
2202(defun allout-chart-siblings (&optional start end)
c567ac01 2203 "Produce a list of locations of this and succeeding sibling topics.
fd5359c6 2204Effectively a top-level chart of siblings. See `allout-chart-subtree'
c567ac01
RS
2205for an explanation of charts."
2206 (save-excursion
fd5359c6 2207 (if (allout-goto-prefix)
c567ac01 2208 (let ((chart (list (point))))
fd5359c6 2209 (while (allout-next-sibling)
c567ac01 2210 (setq chart (cons (point) chart)))
19b84ba3 2211 (if chart (setq chart (nreverse chart)))))))
fd5359c6
MR
2212;;;_ > allout-chart-to-reveal (chart depth)
2213(defun allout-chart-to-reveal (chart depth)
c567ac01 2214
19b84ba3 2215 "Return a flat list of hidden points in subtree CHART, up to DEPTH.
c567ac01 2216
19b84ba3 2217Note that point can be left at any of the points on chart, or at the
c567ac01
RS
2218start point."
2219
2220 (let (result here)
2221 (while (and (or (eq depth t) (> depth 0))
2222 chart)
2223 (setq here (car chart))
2224 (if (listp here)
fd5359c6 2225 (let ((further (allout-chart-to-reveal here (or (eq depth t)
c567ac01
RS
2226 (1- depth)))))
2227 ;; We're on the start of a subtree - recurse with it, if there's
2228 ;; more depth to go:
2229 (if further (setq result (append further result)))
2230 (setq chart (cdr chart)))
2231 (goto-char here)
96b83743 2232 (if (allout-hidden-p)
c567ac01
RS
2233 (setq result (cons here result)))
2234 (setq chart (cdr chart))))
2235 result))
fd5359c6 2236;;;_ X allout-chart-spec (chart spec &optional exposing)
e126900f
JB
2237;; (defun allout-chart-spec (chart spec &optional exposing)
2238;; "Not yet \(if ever) implemented.
2239
2240;; Produce exposure directives given topic/subtree CHART and an exposure SPEC.
2241
2242;; Exposure spec indicates the locations to be exposed and the prescribed
2243;; exposure status. Optional arg EXPOSING is an integer, with 0
2244;; indicating pending concealment, anything higher indicating depth to
2245;; which subtopic headers should be exposed, and negative numbers
2246;; indicating (negative of) the depth to which subtopic headers and
2247;; bodies should be exposed.
2248
2249;; The produced list can have two types of entries. Bare numbers
2250;; indicate points in the buffer where topic headers that should be
2251;; exposed reside.
2252
2253;; - bare negative numbers indicates that the topic starting at the
2254;; point which is the negative of the number should be opened,
2255;; including their entries.
2256;; - bare positive values indicate that this topic header should be
2257;; opened.
2258;; - Lists signify the beginning and end points of regions that should
2259;; be flagged, and the flag to employ. (For concealment: `\(\?r\)', and
2260;; exposure:"
2261;; (while spec
2262;; (cond ((listp spec)
2263;; )
2264;; )
2265;; (setq spec (cdr spec)))
2266;; )
c567ac01
RS
2267
2268;;;_ - Within Topic
fd5359c6
MR
2269;;;_ > allout-goto-prefix ()
2270(defun allout-goto-prefix ()
9179616f 2271 "Put point at beginning of immediately containing outline topic.
19b84ba3 2272
9179616f 2273Goes to most immediate subsequent topic if none immediately containing.
19b84ba3
RS
2274
2275Not sensitive to topic visibility.
c567ac01 2276
d27081f5 2277Returns the point at the beginning of the prefix, or nil if none."
c567ac01 2278
19b84ba3
RS
2279 (let (done)
2280 (while (and (not done)
96b83743 2281 (search-backward "\n" nil 1))
19b84ba3 2282 (forward-char 1)
fd5359c6
MR
2283 (if (looking-at allout-regexp)
2284 (setq done (allout-prefix-data (match-beginning 0)
19b84ba3
RS
2285 (match-end 0)))
2286 (forward-char -1)))
2287 (if (bobp)
fd5359c6
MR
2288 (cond ((looking-at allout-regexp)
2289 (allout-prefix-data (match-beginning 0)(match-end 0)))
2290 ((allout-next-heading))
19b84ba3
RS
2291 (done))
2292 done)))
fd5359c6
MR
2293;;;_ > allout-end-of-prefix ()
2294(defun allout-end-of-prefix (&optional ignore-decorations)
19b84ba3
RS
2295 "Position cursor at beginning of header text.
2296
2297If optional IGNORE-DECORATIONS is non-nil, put just after bullet,
2298otherwise skip white space between bullet and ensuing text."
c567ac01 2299
fd5359c6 2300 (if (not (allout-goto-prefix))
1977b8f6
RS
2301 nil
2302 (let ((match-data (match-data)))
2303 (goto-char (match-end 0))
c567ac01
RS
2304 (if ignore-decorations
2305 t
2306 (while (looking-at "[0-9]") (forward-char 1))
2307 (if (and (not (eolp)) (looking-at "\\s-")) (forward-char 1)))
9179616f 2308 (store-match-data match-data))
1977b8f6 2309 ;; Reestablish where we are:
fd5359c6
MR
2310 (allout-current-depth)))
2311;;;_ > allout-current-bullet-pos ()
2312(defun allout-current-bullet-pos ()
c567ac01
RS
2313 "Return position of current \(visible) topic's bullet."
2314
fd5359c6 2315 (if (not (allout-current-depth))
c567ac01
RS
2316 nil
2317 (1- (match-end 0))))
fd5359c6
MR
2318;;;_ > allout-back-to-current-heading ()
2319(defun allout-back-to-current-heading ()
96b83743
EZ
2320 "Move to heading line of current topic, or beginning if already on the line.
2321
2322Return value of point, unless we started outside of (before any) topics,
2323in which case we return nil."
2324
2325 (allout-beginning-of-current-line)
2326 (if (or (allout-on-current-heading-p)
2327 (and (re-search-backward (concat "^\\(" allout-regexp "\\)")
2328 nil 'move)
2329 (progn (while (allout-hidden-p)
2330 (allout-beginning-of-current-line)
2331 (if (not (looking-at allout-regexp))
2332 (re-search-backward (concat
2333 "^\\(" allout-regexp "\\)")
2334 nil 'move)))
2335 (allout-prefix-data (match-beginning 1)
2336 (match-end 1)))))
2337 (if (interactive-p)
2338 (allout-end-of-prefix)
2339 (point))))
fd5359c6
MR
2340;;;_ > allout-back-to-heading ()
2341(defalias 'allout-back-to-heading 'allout-back-to-current-heading)
96b83743
EZ
2342;;;_ > allout-pre-next-prefix ()
2343(defun allout-pre-next-prefix ()
1977b8f6
RS
2344 "Skip forward to just before the next heading line.
2345
c567ac01 2346Returns that character position."
1977b8f6 2347
fd5359c6 2348 (if (re-search-forward allout-line-boundary-regexp nil 'move)
c567ac01 2349 (prog1 (goto-char (match-beginning 0))
fd5359c6 2350 (allout-prefix-data (match-beginning 2)(match-end 2)))))
96b83743
EZ
2351;;;_ > allout-end-of-subtree (&optional current include-trailing-blank)
2352(defun allout-end-of-subtree (&optional current include-trailing-blank)
d82979ea
EZ
2353 "Put point at the end of the last leaf in the containing topic.
2354
96b83743
EZ
2355Optional CURRENT means put point at the end of the containing
2356visible topic.
2357
2358Optional INCLUDE-TRAILING-BLANK means include a trailing blank line, if
2359any, as part of the subtree. Otherwise, that trailing blank will be
2360excluded as delimiting whitespace between topics.
d82979ea
EZ
2361
2362Returns the value of point."
2363 (interactive "P")
2364 (if current
2365 (allout-back-to-current-heading)
2366 (allout-goto-prefix))
fd5359c6
MR
2367 (let ((level (allout-recent-depth)))
2368 (allout-next-heading)
1977b8f6 2369 (while (and (not (eobp))
fd5359c6
MR
2370 (> (allout-recent-depth) level))
2371 (allout-next-heading))
c567ac01 2372 (and (not (eobp)) (forward-char -1))
96b83743 2373 (if (and (not include-trailing-blank) (= ?\n (preceding-char)))
c567ac01 2374 (forward-char -1))
fd5359c6 2375 (setq allout-recent-end-of-subtree (point))))
96b83743
EZ
2376;;;_ > allout-end-of-current-subtree (&optional include-trailing-blank)
2377(defun allout-end-of-current-subtree (&optional include-trailing-blank)
2378
d82979ea
EZ
2379 "Put point at end of last leaf in currently visible containing topic.
2380
96b83743
EZ
2381Optional INCLUDE-TRAILING-BLANK means include a trailing blank line, if
2382any, as part of the subtree. Otherwise, that trailing blank will be
2383excluded as delimiting whitespace between topics.
2384
d82979ea
EZ
2385Returns the value of point."
2386 (interactive)
96b83743 2387 (allout-end-of-subtree t include-trailing-blank))
fd5359c6
MR
2388;;;_ > allout-beginning-of-current-entry ()
2389(defun allout-beginning-of-current-entry ()
d82979ea 2390 "When not already there, position point at beginning of current topic header.
c567ac01
RS
2391
2392If already there, move cursor to bullet for hot-spot operation.
46771bb3 2393\(See `allout-mode' doc string for details of hot-spot operation.)"
1977b8f6 2394 (interactive)
c567ac01 2395 (let ((start-point (point)))
46771bb3 2396 (move-beginning-of-line 1)
fd5359c6 2397 (allout-end-of-prefix)
c567ac01
RS
2398 (if (and (interactive-p)
2399 (= (point) start-point))
fd5359c6 2400 (goto-char (allout-current-bullet-pos)))))
96b83743
EZ
2401;;;_ > allout-end-of-entry (&optional inclusive)
2402(defun allout-end-of-entry (&optional inclusive)
2403 "Position the point at the end of the current topics' entry.
2404
2405Optional INCLUSIVE means also include trailing empty line, if any. When
2406unset, whitespace between items separates them even when the items are
2407collapsed."
1977b8f6 2408 (interactive)
96b83743
EZ
2409 (allout-pre-next-prefix)
2410 (if (and (not inclusive) (not (bobp)) (= ?\n (preceding-char)))
2411 (forward-char -1))
2412 (point))
fd5359c6
MR
2413;;;_ > allout-end-of-current-heading ()
2414(defun allout-end-of-current-heading ()
9179616f 2415 (interactive)
fd5359c6 2416 (allout-beginning-of-current-entry)
96b83743 2417 (search-forward "\n" nil t)
d82979ea 2418 (forward-char -1))
fd5359c6 2419(defalias 'allout-end-of-heading 'allout-end-of-current-heading)
d82979ea
EZ
2420;;;_ > allout-get-body-text ()
2421(defun allout-get-body-text ()
2422 "Return the unmangled body text of the topic immediately containing point."
2423 (save-excursion
2424 (allout-end-of-prefix)
96b83743 2425 (if (not (search-forward "\n" nil t))
d82979ea
EZ
2426 nil
2427 (backward-char 1)
2428 (let ((pre-body (point)))
2429 (if (not pre-body)
2430 nil
96b83743 2431 (allout-end-of-entry t)
d82979ea
EZ
2432 (if (not (= pre-body (point)))
2433 (buffer-substring-no-properties (1+ pre-body) (point))))
2434 )
2435 )
2436 )
2437 )
1977b8f6 2438
c567ac01 2439;;;_ - Depth-wise
fd5359c6
MR
2440;;;_ > allout-ascend-to-depth (depth)
2441(defun allout-ascend-to-depth (depth)
c567ac01 2442 "Ascend to depth DEPTH, returning depth if successful, nil if not."
fd5359c6 2443 (if (and (> depth 0)(<= depth (allout-depth)))
1977b8f6 2444 (let ((last-good (point)))
fd5359c6 2445 (while (and (< depth (allout-depth))
1977b8f6 2446 (setq last-good (point))
fd5359c6
MR
2447 (allout-beginning-of-level)
2448 (allout-previous-heading)))
2449 (if (= (allout-recent-depth) depth)
2450 (progn (goto-char allout-recent-prefix-beginning)
1977b8f6 2451 depth)
d82979ea
EZ
2452 (goto-char last-good)
2453 nil))
2454 (if (interactive-p) (allout-end-of-prefix))))
fd5359c6
MR
2455;;;_ > allout-ascend ()
2456(defun allout-ascend ()
9179616f 2457 "Ascend one level, returning t if successful, nil if not."
d82979ea
EZ
2458 (prog1
2459 (if (allout-beginning-of-level)
2460 (allout-previous-heading))
2461 (if (interactive-p) (allout-end-of-prefix))))
fd5359c6
MR
2462;;;_ > allout-descend-to-depth (depth)
2463(defun allout-descend-to-depth (depth)
19b84ba3
RS
2464 "Descend to depth DEPTH within current topic.
2465
2466Returning depth if successful, nil if not."
1977b8f6 2467 (let ((start-point (point))
fd5359c6 2468 (start-depth (allout-depth)))
1977b8f6 2469 (while
fd5359c6
MR
2470 (and (> (allout-depth) 0)
2471 (not (= depth (allout-recent-depth))) ; ... not there yet
2472 (allout-next-heading) ; ... go further
2473 (< start-depth (allout-recent-depth)))) ; ... still in topic
2474 (if (and (> (allout-depth) 0)
2475 (= (allout-recent-depth) depth))
1977b8f6
RS
2476 depth
2477 (goto-char start-point)
2478 nil))
2479 )
fd5359c6 2480;;;_ > allout-up-current-level (arg &optional dont-complain)
d82979ea 2481(defun allout-up-current-level (arg &optional dont-complain)
19b84ba3
RS
2482 "Move out ARG levels from current visible topic.
2483
2484Positions on heading line of containing topic. Error if unable to
2485ascend that far, or nil if unable to ascend but optional arg
2486DONT-COMPLAIN is non-nil."
d82979ea 2487 (interactive "p")
fd5359c6
MR
2488 (allout-back-to-current-heading)
2489 (let ((present-level (allout-recent-depth))
19b84ba3 2490 (last-good (point))
96b83743 2491 failed)
1977b8f6 2492 ;; Loop for iterating arg:
fd5359c6 2493 (while (and (> (allout-recent-depth) 1)
1977b8f6 2494 (> arg 0)
19b84ba3
RS
2495 (not (bobp))
2496 (not failed))
2497 (setq last-good (point))
1977b8f6 2498 ;; Loop for going back over current or greater depth:
fd5359c6
MR
2499 (while (and (not (< (allout-recent-depth) present-level))
2500 (or (allout-previous-visible-heading 1)
19b84ba3 2501 (not (setq failed present-level)))))
fd5359c6 2502 (setq present-level (allout-current-depth))
1977b8f6 2503 (setq arg (- arg 1)))
19b84ba3
RS
2504 (if (or failed
2505 (> arg 0))
2506 (progn (goto-char last-good)
d82979ea 2507 (if (interactive-p) (allout-end-of-prefix))
19b84ba3 2508 (if (not dont-complain)
6a05d05f 2509 (error "Can't ascend past outermost level")
d82979ea 2510 (if (interactive-p) (allout-end-of-prefix))
19b84ba3 2511 nil))
d82979ea 2512 (if (interactive-p) (allout-end-of-prefix))
fd5359c6 2513 allout-recent-prefix-beginning)))
1977b8f6 2514
c567ac01 2515;;;_ - Linear
fd5359c6
MR
2516;;;_ > allout-next-sibling (&optional depth backward)
2517(defun allout-next-sibling (&optional depth backward)
2265e017 2518 "Like `allout-forward-current-level', but respects invisible topics.
1977b8f6 2519
c567ac01 2520Traverse at optional DEPTH, or current depth if none specified.
1977b8f6 2521
c567ac01 2522Go backward if optional arg BACKWARD is non-nil.
1977b8f6 2523
c567ac01 2524Return depth if successful, nil otherwise."
1977b8f6
RS
2525
2526 (if (and backward (bobp))
2527 nil
fd5359c6 2528 (let ((start-depth (or depth (allout-depth)))
1977b8f6 2529 (start-point (point))
c567ac01 2530 last-depth)
1977b8f6 2531 (while (and (not (if backward (bobp) (eobp)))
fd5359c6
MR
2532 (if backward (allout-previous-heading)
2533 (allout-next-heading))
2534 (> (setq last-depth (allout-recent-depth)) start-depth)))
1977b8f6 2535 (if (and (not (eobp))
fd5359c6
MR
2536 (and (> (or last-depth (allout-depth)) 0)
2537 (= (allout-recent-depth) start-depth)))
2538 allout-recent-prefix-beginning
1977b8f6 2539 (goto-char start-point)
fd5359c6 2540 (if depth (allout-depth) start-depth)
c567ac01 2541 nil))))
fd5359c6
MR
2542;;;_ > allout-previous-sibling (&optional depth backward)
2543(defun allout-previous-sibling (&optional depth backward)
d82979ea 2544 "Like `allout-forward-current-level' backwards, respecting invisible topics.
1977b8f6 2545
c567ac01 2546Optional DEPTH specifies depth to traverse, default current depth.
1977b8f6 2547
c567ac01 2548Optional BACKWARD reverses direction.
1977b8f6 2549
c567ac01 2550Return depth if successful, nil otherwise."
fd5359c6 2551 (allout-next-sibling depth (not backward))
1977b8f6 2552 )
fd5359c6
MR
2553;;;_ > allout-snug-back ()
2554(defun allout-snug-back ()
539d7736 2555 "Position cursor at end of previous topic.
19b84ba3
RS
2556
2557Presumes point is at the start of a topic prefix."
c567ac01
RS
2558 (if (or (bobp) (eobp))
2559 nil
2560 (forward-char -1))
96b83743 2561 (if (or (bobp) (not (= ?\n (preceding-char))))
c567ac01 2562 nil
96b83743 2563 (forward-char -1))
c567ac01 2564 (point))
fd5359c6
MR
2565;;;_ > allout-beginning-of-level ()
2566(defun allout-beginning-of-level ()
c567ac01 2567 "Go back to the first sibling at this level, visible or not."
fd5359c6
MR
2568 (allout-end-of-level 'backward))
2569;;;_ > allout-end-of-level (&optional backward)
2570(defun allout-end-of-level (&optional backward)
c567ac01 2571 "Go to the last sibling at this level, visible or not."
1977b8f6 2572
fd5359c6
MR
2573 (let ((depth (allout-depth)))
2574 (while (allout-previous-sibling depth nil))
2575 (prog1 (allout-recent-depth)
d82979ea 2576 (if (interactive-p) (allout-end-of-prefix)))))
fd5359c6
MR
2577;;;_ > allout-next-visible-heading (arg)
2578(defun allout-next-visible-heading (arg)
19b84ba3 2579 "Move to the next ARG'th visible heading line, backward if arg is negative.
c567ac01 2580
96b83743 2581Move to buffer limit in indicated direction if headings are exhausted."
1977b8f6 2582
1977b8f6 2583 (interactive "p")
0458fa80
EZ
2584 (let* ((inhibit-field-text-motion t)
2585 (backward (if (< arg 0) (setq arg (* -1 arg))))
c567ac01
RS
2586 (step (if backward -1 1))
2587 prev got)
2588
2589 (while (> arg 0) ; limit condition
2590 (while (and (not (if backward (bobp)(eobp))) ; boundary condition
19b84ba3 2591 ;; Move, skipping over all those concealed lines:
96b83743
EZ
2592 (prog1 (condition-case nil (or (line-move step) t)
2593 (error nil))
2594 (allout-beginning-of-current-line))
fd5359c6 2595 (not (setq got (looking-at allout-regexp)))))
c567ac01
RS
2596 ;; Register this got, it may be the last:
2597 (if got (setq prev got))
2598 (setq arg (1- arg)))
2599 (cond (got ; Last move was to a prefix:
fd5359c6
MR
2600 (allout-prefix-data (match-beginning 0) (match-end 0))
2601 (allout-end-of-prefix))
c567ac01 2602 (prev ; Last move wasn't, but prev was:
fd5359c6 2603 (allout-prefix-data (match-beginning 0) (match-end 0)))
c567ac01 2604 ((not backward) (end-of-line) nil))))
fd5359c6
MR
2605;;;_ > allout-previous-visible-heading (arg)
2606(defun allout-previous-visible-heading (arg)
c567ac01 2607 "Move to the previous heading line.
1977b8f6 2608
c567ac01 2609With argument, repeats or can move forward if negative.
2265e017 2610A heading line is one that starts with a `*' (or that `allout-regexp'
c567ac01 2611matches)."
1977b8f6 2612 (interactive "p")
fd5359c6
MR
2613 (allout-next-visible-heading (- arg)))
2614;;;_ > allout-forward-current-level (arg)
d82979ea 2615(defun allout-forward-current-level (arg)
19b84ba3 2616 "Position point at the next heading of the same level.
1977b8f6 2617
19b84ba3 2618Takes optional repeat-count, goes backward if count is negative.
1977b8f6 2619
19b84ba3 2620Returns resulting position, else nil if none found."
d82979ea 2621 (interactive "p")
fd5359c6 2622 (let ((start-depth (allout-current-depth))
19b84ba3
RS
2623 (start-arg arg)
2624 (backward (> 0 arg))
2625 last-depth
2626 (last-good (point))
2627 at-boundary)
2628 (if (= 0 start-depth)
2629 (error "No siblings, not in a topic..."))
2630 (if backward (setq arg (* -1 arg)))
2631 (while (not (or (zerop arg)
2632 at-boundary))
2633 (while (and (not (if backward (bobp) (eobp)))
fd5359c6
MR
2634 (if backward (allout-previous-visible-heading 1)
2635 (allout-next-visible-heading 1))
2636 (> (setq last-depth (allout-recent-depth)) start-depth)))
19b84ba3
RS
2637 (if (and last-depth (= last-depth start-depth)
2638 (not (if backward (bobp) (eobp))))
2639 (setq last-good (point)
2640 arg (1- arg))
2641 (setq at-boundary t)))
2642 (if (and (not (eobp))
2643 (= arg 0)
fd5359c6
MR
2644 (and (> (or last-depth (allout-depth)) 0)
2645 (= (allout-recent-depth) start-depth)))
2646 allout-recent-prefix-beginning
19b84ba3 2647 (goto-char last-good)
d82979ea 2648 (if (not (interactive-p))
19b84ba3 2649 nil
fd5359c6 2650 (allout-end-of-prefix)
6a05d05f 2651 (error "Hit %s level %d topic, traversed %d of %d requested"
19b84ba3 2652 (if backward "first" "last")
fd5359c6 2653 (allout-recent-depth)
19b84ba3
RS
2654 (- (abs start-arg) arg)
2655 (abs start-arg))))))
fd5359c6 2656;;;_ > allout-backward-current-level (arg)
d82979ea 2657(defun allout-backward-current-level (arg)
fd5359c6 2658 "Inverse of `allout-forward-current-level'."
d82979ea
EZ
2659 (interactive "p")
2660 (if (interactive-p)
19b84ba3 2661 (let ((current-prefix-arg (* -1 arg)))
fd5359c6
MR
2662 (call-interactively 'allout-forward-current-level))
2663 (allout-forward-current-level (* -1 arg))))
c567ac01 2664
19b84ba3 2665;;;_ #5 Alteration
c567ac01
RS
2666
2667;;;_ - Fundamental
fd5359c6
MR
2668;;;_ = allout-post-goto-bullet
2669(defvar allout-post-goto-bullet nil
2670 "Outline internal var, for `allout-pre-command-business' hot-spot operation.
19b84ba3
RS
2671
2672When set, tells post-processing to reposition on topic bullet, and
2265e017 2673then unset it. Set by `allout-pre-command-business' when implementing
19b84ba3
RS
2674hot-spot operation, where literal characters typed over a topic bullet
2675are mapped to the command of the corresponding control-key on the
2265e017 2676`allout-mode-map'.")
fd5359c6
MR
2677(make-variable-buffer-local 'allout-post-goto-bullet)
2678;;;_ > allout-post-command-business ()
2679(defun allout-post-command-business ()
2265e017 2680 "Outline `post-command-hook' function.
c567ac01 2681
2265e017 2682- Implement (and clear) `allout-post-goto-bullet', for hot-spot
c567ac01
RS
2683 outline commands.
2684
96b83743 2685- Decrypt topic currently being edited if it was encrypted for a save."
c567ac01
RS
2686
2687 ; Apply any external change func:
fd5359c6 2688 (if (not (allout-mode-p)) ; In allout-mode.
19b84ba3 2689 nil
d82979ea
EZ
2690
2691 (if (and (boundp 'allout-after-save-decrypt)
2692 allout-after-save-decrypt)
2693 (allout-after-saves-handler))
2694
96b83743 2695 ;; Implement -post-goto-bullet, if set:
fd5359c6
MR
2696 (if (and allout-post-goto-bullet
2697 (allout-current-bullet-pos))
2698 (progn (goto-char (allout-current-bullet-pos))
2699 (setq allout-post-goto-bullet nil)))
19b84ba3 2700 ))
fd5359c6
MR
2701;;;_ > allout-pre-command-business ()
2702(defun allout-pre-command-business ()
2265e017 2703 "Outline `pre-command-hook' function for outline buffers.
58edceeb 2704Implements special behavior when cursor is on bullet character.
c567ac01 2705
58edceeb
JB
2706When the cursor is on the bullet character, self-insert characters are
2707reinterpreted as the corresponding control-character in the
2708`allout-mode-map'. The `allout-mode' `post-command-hook' insures that
2709the cursor which has moved as a result of such reinterpretation is
2710positioned on the bullet character of the destination topic.
c567ac01 2711
9179616f
DL
2712The upshot is that you can get easy, single (ie, unmodified) key
2713outline maneuvering operations by positioning the cursor on the bullet
58edceeb
JB
2714char. When in this mode you can use regular cursor-positioning
2715command/keystrokes to relocate the cursor off of a bullet character to
2716return to regular interpretation of self-insert characters."
d82979ea 2717
fd5359c6 2718 (if (not (allout-mode-p))
9179616f 2719 nil
9179616f 2720 (if (and (eq this-command 'self-insert-command)
fd5359c6 2721 (eq (point)(allout-current-bullet-pos)))
01fc9422
EZ
2722 (allout-hotspot-key-handler))))
2723;;;_ > allout-hotspot-key-handler ()
2724(defun allout-hotspot-key-handler ()
2725 "Catchall handling of key bindings in hot-spots.
2726
2727Translates unmodified keystrokes to corresponding allout commands, when
2728they would qualify if prefixed with the allout-command-prefix, and sets
2729this-command accordingly.
2730
2731Returns the qualifying command, if any, else nil."
2732 (interactive)
2733 (let* ((key-num (cond ((numberp last-command-char) last-command-char)
2734 ;; for XEmacs character type:
2735 ((and (fboundp 'characterp)
2736 (apply 'characterp (list last-command-char)))
2737 (apply 'char-to-int (list last-command-char)))
2738 (t 0)))
2739 mapped-binding
2740 (on-bullet (eq (point) (allout-current-bullet-pos))))
2741
2742 (if (zerop key-num)
2743 nil
2744
2745 (if (and (<= 33 key-num)
2746 (setq mapped-binding
2747 (key-binding (concat allout-command-prefix
2748 (char-to-string
2749 (if (and (<= 97 key-num) ; "a"
2750 (>= 122 key-num)) ; "z"
2751 (- key-num 96) key-num)))
2752 t)))
2753 ;; Qualified with the allout prefix - do hot-spot operation.
2754 (setq allout-post-goto-bullet t)
2755 ;; accept-defaults nil, or else we'll get allout-item-icon-key-handler.
2756 (setq mapped-binding (key-binding (char-to-string key-num))))
2757
2758 (while (keymapp mapped-binding)
2759 (setq mapped-binding
2760 (lookup-key mapped-binding (read-key-sequence-vector nil t))))
2761
2762 (if mapped-binding
5204800f 2763 (setq this-command mapped-binding)))))
01fc9422 2764
fd5359c6
MR
2765;;;_ > allout-find-file-hook ()
2766(defun allout-find-file-hook ()
4034b0e2 2767 "Activate `allout-mode' on non-nil `allout-auto-activation', `allout-layout'.
fd5359c6
MR
2768
2769See `allout-init' for setup instructions."
2770 (if (and allout-auto-activation
2771 (not (allout-mode-p))
2772 allout-layout)
2773 (allout-mode t)))
c567ac01
RS
2774
2775;;;_ - Topic Format Assessment
fd5359c6
MR
2776;;;_ > allout-solicit-alternate-bullet (depth &optional current-bullet)
2777(defun allout-solicit-alternate-bullet (depth &optional current-bullet)
1977b8f6 2778
19b84ba3
RS
2779 "Prompt for and return a bullet char as an alternative to the current one.
2780
2781Offer one suitable for current depth DEPTH as default."
1977b8f6 2782
9179616f 2783 (let* ((default-bullet (or (and (stringp current-bullet) current-bullet)
fd5359c6
MR
2784 (allout-bullet-for-depth depth)))
2785 (sans-escapes (regexp-sans-escapes allout-bullets-string))
9179616f
DL
2786 choice)
2787 (save-excursion
fd5359c6 2788 (goto-char (allout-current-bullet-pos))
9179616f
DL
2789 (setq choice (solicit-char-in-string
2790 (format "Select bullet: %s ('%s' default): "
2791 sans-escapes
2792 default-bullet)
2793 sans-escapes
2794 t)))
2795 (message "")
1977b8f6
RS
2796 (if (string= choice "") default-bullet choice))
2797 )
fd5359c6
MR
2798;;;_ > allout-distinctive-bullet (bullet)
2799(defun allout-distinctive-bullet (bullet)
e126900f 2800 "True if BULLET is one of those on `allout-distinctive-bullets-string'."
fd5359c6
MR
2801 (string-match (regexp-quote bullet) allout-distinctive-bullets-string))
2802;;;_ > allout-numbered-type-prefix (&optional prefix)
2803(defun allout-numbered-type-prefix (&optional prefix)
c567ac01 2804 "True if current header prefix bullet is numbered bullet."
fd5359c6
MR
2805 (and allout-numbered-bullet
2806 (string= allout-numbered-bullet
1977b8f6 2807 (if prefix
fd5359c6
MR
2808 (allout-get-prefix-bullet prefix)
2809 (allout-get-bullet)))))
d82979ea
EZ
2810;;;_ > allout-encrypted-type-prefix (&optional prefix)
2811(defun allout-encrypted-type-prefix (&optional prefix)
2812 "True if current header prefix bullet is for an encrypted entry \(body)."
2813 (and allout-topic-encryption-bullet
2814 (string= allout-topic-encryption-bullet
2815 (if prefix
2816 (allout-get-prefix-bullet prefix)
2817 (allout-get-bullet)))))
fd5359c6
MR
2818;;;_ > allout-bullet-for-depth (&optional depth)
2819(defun allout-bullet-for-depth (&optional depth)
19b84ba3 2820 "Return outline topic bullet suited to optional DEPTH, or current depth."
1977b8f6 2821 ;; Find bullet in plain-bullets-string modulo DEPTH.
fd5359c6
MR
2822 (if allout-stylish-prefixes
2823 (char-to-string (aref allout-plain-bullets-string
1977b8f6 2824 (% (max 0 (- depth 2))
fd5359c6
MR
2825 allout-plain-bullets-string-len)))
2826 allout-primary-bullet)
1977b8f6
RS
2827 )
2828
c567ac01 2829;;;_ - Topic Production
fd5359c6
MR
2830;;;_ > allout-make-topic-prefix (&optional prior-bullet
2831(defun allout-make-topic-prefix (&optional prior-bullet
1977b8f6
RS
2832 new
2833 depth
2834 solicit
2835 number-control
2836 index)
2837 ;; Depth null means use current depth, non-null means we're either
2838 ;; opening a new topic after current topic, lower or higher, or we're
2839 ;; changing level of current topic.
2840 ;; Solicit dominates specified bullet-char.
c567ac01 2841;;;_ . Doc string:
19b84ba3 2842 "Generate a topic prefix suitable for optional arg DEPTH, or current depth.
c567ac01
RS
2843
2844All the arguments are optional.
2845
2846PRIOR-BULLET indicates the bullet of the prefix being changed, or
2847nil if none. This bullet may be preserved (other options
2265e017 2848notwithstanding) if it is on the `allout-distinctive-bullets-string',
c567ac01
RS
2849for instance.
2850
2851Second arg NEW indicates that a new topic is being opened after the
2852topic at point, if non-nil. Default bullet for new topics, eg, may
2853be set (contingent to other args) to numbered bullets if previous
2854sibling is one. The implication otherwise is that the current topic
2855is being adjusted - shifted or rebulleted - and we don't consider
2856bullet or previous sibling.
2857
2858Third arg DEPTH forces the topic prefix to that depth, regardless of
2859the current topics' depth.
2860
9179616f
DL
2861If SOLICIT is non-nil, then the choice of bullet is solicited from
2862user. If it's a character, then that character is offered as the
2863default, otherwise the one suited to the context \(according to
2864distinction or depth) is offered. \(This overrides other options,
2865including, eg, a distinctive PRIOR-BULLET.) If non-nil, then the
2866context-specific bullet is used.
c567ac01 2867
fd5359c6 2868Fifth arg, NUMBER-CONTROL, matters only if `allout-numbered-bullet'
c567ac01
RS
2869is non-nil *and* soliciting was not explicitly invoked. Then
2870NUMBER-CONTROL non-nil forces prefix to either numbered or
2871denumbered format, depending on the value of the sixth arg, INDEX.
2872
2873\(Note that NUMBER-CONTROL does *not* apply to level 1 topics. Sorry...)
2874
2875If NUMBER-CONTROL is non-nil and sixth arg INDEX is non-nil then
2876the prefix of the topic is forced to be numbered. Non-nil
2877NUMBER-CONTROL and nil INDEX forces non-numbered format on the
2878bullet. Non-nil NUMBER-CONTROL and non-nil, non-number INDEX means
2879that the index for the numbered prefix will be derived, by counting
2880siblings back to start of level. If INDEX is a number, then that
2881number is used as the index for the numbered prefix (allowing, eg,
a0776d6b 2882sequential renumbering to not require this function counting back the
c567ac01
RS
2883index for each successive sibling)."
2884;;;_ . Code:
1977b8f6
RS
2885 ;; The options are ordered in likely frequence of use, most common
2886 ;; highest, least lowest. Ie, more likely to be doing prefix
2887 ;; adjustments than soliciting, and yet more than numbering.
2888 ;; Current prefix is least dominant, but most likely to be commonly
2889 ;; specified...
2890
2891 (let* (body
2892 numbering
2893 denumbering
fd5359c6
MR
2894 (depth (or depth (allout-depth)))
2895 (header-lead allout-header-prefix)
1977b8f6
RS
2896 (bullet-char
2897
2898 ;; Getting value for bullet char is practically the whole job:
2899
2900 (cond
2901 ; Simplest situation - level 1:
fd5359c6 2902 ((<= depth 1) (setq header-lead "") allout-primary-bullet)
1977b8f6 2903 ; Simple, too: all asterisks:
fd5359c6 2904 (allout-old-style-prefixes
1977b8f6
RS
2905 ;; Cheat - make body the whole thing, null out header-lead and
2906 ;; bullet-char:
2907 (setq body (make-string depth
fd5359c6 2908 (string-to-char allout-primary-bullet)))
1977b8f6
RS
2909 (setq header-lead "")
2910 "")
2911
2912 ;; (Neither level 1 nor old-style, so we're space padding.
2913 ;; Sneak it in the condition of the next case, whatever it is.)
2914
2915 ;; Solicitation overrides numbering and other cases:
2916 ((progn (setq body (make-string (- depth 2) ?\ ))
2917 ;; The actual condition:
2918 solicit)
fd5359c6 2919 (let* ((got (allout-solicit-alternate-bullet depth solicit)))
1977b8f6 2920 ;; Gotta check whether we're numbering and got a numbered bullet:
fd5359c6 2921 (setq numbering (and allout-numbered-bullet
1977b8f6 2922 (not (and number-control (not index)))
fd5359c6 2923 (string= got allout-numbered-bullet)))
1977b8f6
RS
2924 ;; Now return what we got, regardless:
2925 got))
2926
2927 ;; Numbering invoked through args:
fd5359c6 2928 ((and allout-numbered-bullet number-control)
1977b8f6 2929 (if (setq numbering (not (setq denumbering (not index))))
fd5359c6 2930 allout-numbered-bullet
19b84ba3 2931 (if (and prior-bullet
fd5359c6 2932 (not (string= allout-numbered-bullet
19b84ba3
RS
2933 prior-bullet)))
2934 prior-bullet
fd5359c6 2935 (allout-bullet-for-depth depth))))
1977b8f6
RS
2936
2937 ;;; Neither soliciting nor controlled numbering ;;;
2938 ;;; (may be controlled denumbering, tho) ;;;
2939
2940 ;; Check wrt previous sibling:
2941 ((and new ; only check for new prefixes
fd5359c6
MR
2942 (<= depth (allout-depth))
2943 allout-numbered-bullet ; ... & numbering enabled
1977b8f6
RS
2944 (not denumbering)
2945 (let ((sibling-bullet
2946 (save-excursion
2947 ;; Locate correct sibling:
fd5359c6
MR
2948 (or (>= depth (allout-depth))
2949 (allout-ascend-to-depth depth))
2950 (allout-get-bullet))))
1977b8f6 2951 (if (and sibling-bullet
fd5359c6 2952 (string= allout-numbered-bullet sibling-bullet))
1977b8f6
RS
2953 (setq numbering sibling-bullet)))))
2954
2955 ;; Distinctive prior bullet?
2956 ((and prior-bullet
fd5359c6 2957 (allout-distinctive-bullet prior-bullet)
1977b8f6 2958 ;; Either non-numbered:
fd5359c6
MR
2959 (or (not (and allout-numbered-bullet
2960 (string= prior-bullet allout-numbered-bullet)))
1977b8f6
RS
2961 ;; or numbered, and not denumbering:
2962 (setq numbering (not denumbering)))
2963 ;; Here 'tis:
2964 prior-bullet))
2965
2966 ;; Else, standard bullet per depth:
fd5359c6 2967 ((allout-bullet-for-depth depth)))))
1977b8f6
RS
2968
2969 (concat header-lead
2970 body
2971 bullet-char
2972 (if numbering
2973 (format "%d" (cond ((and index (numberp index)) index)
fd5359c6
MR
2974 (new (1+ (allout-sibling-index depth)))
2975 ((allout-sibling-index))))))
1977b8f6
RS
2976 )
2977 )
96b83743
EZ
2978;;;_ > allout-open-topic (relative-depth &optional before offer-recent-bullet)
2979(defun allout-open-topic (relative-depth &optional before offer-recent-bullet)
d82979ea 2980 "Open a new topic at depth DEPTH.
19b84ba3
RS
2981
2982New topic is situated after current one, unless optional flag BEFORE
96b83743
EZ
2983is non-nil, or unless current line is completely empty - lacking even
2984whitespace - in which case open is done on the current line.
2985
2986When adding an offspring, it will be added immediately after the parent if
2987the other offspring are exposed, or after the last child if the offspring
2988are hidden. \(The intervening offspring will be exposed in the latter
2989case.)
c567ac01 2990
96b83743 2991If OFFER-RECENT-BULLET is true, offer to use the bullet of the prior sibling.
9179616f 2992
c567ac01
RS
2993Nuances:
2994
2995- Creation of new topics is with respect to the visible topic
2996 containing the cursor, regardless of intervening concealed ones.
2997
2998- New headers are generally created after/before the body of a
2999 topic. However, they are created right at cursor location if the
3000 cursor is on a blank line, even if that breaks the current topic
3001 body. This is intentional, to provide a simple means for
3002 deliberately dividing topic bodies.
3003
3004- Double spacing of topic lists is preserved. Also, the first
3005 level two topic is created double-spaced (and so would be
3006 subsequent siblings, if that's left intact). Otherwise,
3007 single-spacing is used.
3008
3009- Creation of sibling or nested topics is with respect to the topic
3010 you're starting from, even when creating backwards. This way you
3011 can easily create a sibling in front of the current topic without
a0776d6b 3012 having to go to its preceding sibling, and then open forward
c567ac01 3013 from there."
1977b8f6 3014
96b83743 3015 (allout-beginning-of-current-line)
0458fa80
EZ
3016 (let* ((inhibit-field-text-motion t)
3017 (depth (+ (allout-current-depth) relative-depth))
1977b8f6
RS
3018 (opening-on-blank (if (looking-at "^\$")
3019 (not (setq before nil))))
d82979ea
EZ
3020 ;; bunch o vars set while computing ref-topic
3021 opening-numbered
d82979ea
EZ
3022 ref-depth
3023 ref-bullet
1977b8f6
RS
3024 (ref-topic (save-excursion
3025 (cond ((< relative-depth 0)
fd5359c6 3026 (allout-ascend-to-depth depth))
1977b8f6 3027 ((>= relative-depth 1) nil)
fd5359c6
MR
3028 (t (allout-back-to-current-heading)))
3029 (setq ref-depth (allout-recent-depth))
9179616f 3030 (setq ref-bullet
fd5359c6
MR
3031 (if (> allout-recent-prefix-end 1)
3032 (allout-recent-bullet)
9179616f 3033 ""))
1977b8f6
RS
3034 (setq opening-numbered
3035 (save-excursion
fd5359c6 3036 (and allout-numbered-bullet
1977b8f6 3037 (or (<= relative-depth 0)
fd5359c6
MR
3038 (allout-descend-to-depth depth))
3039 (if (allout-numbered-type-prefix)
3040 allout-numbered-bullet))))
1977b8f6
RS
3041 (point)))
3042 dbl-space
c567ac01 3043 doing-beginning)
1977b8f6
RS
3044
3045 (if (not opening-on-blank)
3046 ; Positioning and vertical
3047 ; padding - only if not
3048 ; opening-on-blank:
353e2ef2 3049 (progn
1977b8f6
RS
3050 (goto-char ref-topic)
3051 (setq dbl-space ; Determine double space action:
c567ac01
RS
3052 (or (and (<= relative-depth 0) ; not descending;
3053 (save-excursion
a0776d6b 3054 ;; at b-o-b or preceded by a blank line?
c567ac01
RS
3055 (or (> 0 (forward-line -1))
3056 (looking-at "^\\s-*$")
3057 (bobp)))
1977b8f6 3058 (save-excursion
c567ac01 3059 ;; succeeded by a blank line?
fd5359c6 3060 (allout-end-of-current-subtree)
96b83743 3061 (looking-at "\n\n")))
1977b8f6
RS
3062 (and (= ref-depth 1)
3063 (or before
3064 (= depth 1)
3065 (save-excursion
3066 ;; Don't already have following
3067 ;; vertical padding:
96b83743 3068 (not (allout-pre-next-prefix)))))))
1977b8f6 3069
96b83743
EZ
3070 ;; Position to prior heading, if inserting backwards, and not
3071 ;; going outwards:
c567ac01 3072 (if (and before (>= relative-depth 0))
fd5359c6 3073 (progn (allout-back-to-current-heading)
1977b8f6 3074 (setq doing-beginning (bobp))
c567ac01 3075 (if (not (bobp))
fd5359c6 3076 (allout-previous-heading)))
c567ac01 3077 (if (and before (bobp))
96b83743 3078 (open-line 1)))
1977b8f6 3079
c567ac01 3080 (if (<= relative-depth 0)
1977b8f6
RS
3081 ;; Not going inwards, don't snug up:
3082 (if doing-beginning
96b83743
EZ
3083 (if (not dbl-space)
3084 (open-line 1)
3085 (open-line 2))
c567ac01
RS
3086 (if before
3087 (progn (end-of-line)
96b83743
EZ
3088 (allout-pre-next-prefix)
3089 (while (and (= ?\n (following-char))
3090 (save-excursion
3091 (forward-char 1)
3092 (allout-hidden-p)))
c567ac01
RS
3093 (forward-char 1))
3094 (if (not (looking-at "^$"))
96b83743
EZ
3095 (open-line 1)))
3096 (allout-end-of-current-subtree)
3097 (if (looking-at "\n\n") (forward-char 1))))
3098 ;; Going inwards - double-space if first offspring is
3099 ;; double-spaced, otherwise snug up.
3100 (allout-end-of-entry)
46771bb3
EZ
3101 (if (eobp)
3102 (newline 1)
3103 (line-move 1))
96b83743
EZ
3104 (allout-beginning-of-current-line)
3105 (backward-char 1)
1977b8f6
RS
3106 (if (bolp)
3107 ;; Blank lines between current header body and next
3108 ;; header - get to last substantive (non-white-space)
3109 ;; line in body:
96b83743
EZ
3110 (progn (setq dbl-space t)
3111 (re-search-backward "[^ \t\n]" nil t)))
3112 (if (looking-at "\n\n")
3113 (setq dbl-space t))
1977b8f6 3114 (if (save-excursion
fd5359c6 3115 (allout-next-heading)
96b83743
EZ
3116 (when (> (allout-recent-depth) ref-depth)
3117 ;; This is an offspring.
3118 (forward-line -1)
3119 (looking-at "^\\s-*$")))
1977b8f6 3120 (progn (forward-line 1)
96b83743 3121 (open-line 1)
d82979ea 3122 (forward-line 1)))
96b83743
EZ
3123 (allout-end-of-current-line))
3124
1977b8f6 3125 ;;(if doing-beginning (goto-char doing-beginning))
c567ac01 3126 (if (not (bobp))
d82979ea
EZ
3127 ;; We insert a newline char rather than using open-line to
3128 ;; avoid rear-stickiness inheritence of read-only property.
c567ac01
RS
3129 (progn (if (and (not (> depth ref-depth))
3130 (not before))
96b83743
EZ
3131 (open-line 1)
3132 (if (and (not dbl-space) (> depth ref-depth))
3133 (newline 1)
c567ac01 3134 (if dbl-space
96b83743 3135 (open-line 1)
c567ac01 3136 (if (not before)
96b83743
EZ
3137 (newline 1)))))
3138 (if (and dbl-space (not (> relative-depth 0)))
3139 (newline 1))
c567ac01
RS
3140 (if (and (not (eobp))
3141 (not (bolp)))
3142 (forward-char 1))))
1977b8f6 3143 ))
96b83743
EZ
3144 (insert (concat (allout-make-topic-prefix opening-numbered t depth)
3145 " "))
3146
3147 (allout-rebullet-heading (and offer-recent-bullet ref-bullet)
3148 depth nil nil t)
3149 (if (> relative-depth 0)
3150 (save-excursion (goto-char ref-topic)
3151 (allout-show-children)))
d82979ea 3152 (end-of-line)
1977b8f6
RS
3153 )
3154 )
fd5359c6
MR
3155;;;_ > allout-open-subtopic (arg)
3156(defun allout-open-subtopic (arg)
c567ac01
RS
3157 "Open new topic header at deeper level than the current one.
3158
3159Negative universal arg means to open deeper, but place the new topic
3160prior to the current one."
1977b8f6 3161 (interactive "p")
d82979ea 3162 (allout-open-topic 1 (> 0 arg) (< 1 arg)))
fd5359c6
MR
3163;;;_ > allout-open-sibtopic (arg)
3164(defun allout-open-sibtopic (arg)
19b84ba3
RS
3165 "Open new topic header at same level as the current one.
3166
9179616f
DL
3167Positive universal arg means to use the bullet of the prior sibling.
3168
19b84ba3 3169Negative universal arg means to place the new topic prior to the current
c567ac01 3170one."
1977b8f6 3171 (interactive "p")
d82979ea 3172 (allout-open-topic 0 (> 0 arg) (not (= 1 arg))))
fd5359c6
MR
3173;;;_ > allout-open-supertopic (arg)
3174(defun allout-open-supertopic (arg)
c567ac01 3175 "Open new topic header at shallower level than the current one.
19b84ba3 3176
c567ac01
RS
3177Negative universal arg means to open shallower, but place the new
3178topic prior to the current one."
1977b8f6
RS
3179
3180 (interactive "p")
d82979ea 3181 (allout-open-topic -1 (> 0 arg) (< 1 arg)))
c567ac01
RS
3182
3183;;;_ - Outline Alteration
3184;;;_ : Topic Modification
fd5359c6
MR
3185;;;_ = allout-former-auto-filler
3186(defvar allout-former-auto-filler nil
2265e017 3187 "Name of modal fill function being wrapped by `allout-auto-fill'.")
fd5359c6
MR
3188;;;_ > allout-auto-fill ()
3189(defun allout-auto-fill ()
aad94676 3190 "`allout-mode' autofill function.
19b84ba3
RS
3191
3192Maintains outline hanging topic indentation if
fd5359c6 3193`allout-use-hanging-indents' is set."
01fc9422
EZ
3194
3195 (when (not allout-inhibit-auto-fill)
3196 (let ((fill-prefix (if allout-use-hanging-indents
3197 ;; Check for topic header indentation:
3198 (save-excursion
3199 (beginning-of-line)
3200 (if (looking-at allout-regexp)
3201 ;; ... construct indentation to account for
3202 ;; length of topic prefix:
3203 (make-string (progn (allout-end-of-prefix)
3204 (current-column))
3205 ?\ )))))
3206 (use-auto-fill-function (or allout-outside-normal-auto-fill-function
3207 auto-fill-function
3208 'do-auto-fill)))
3209 (if (or allout-former-auto-filler allout-use-hanging-indents)
3210 (funcall use-auto-fill-function)))))
fd5359c6
MR
3211;;;_ > allout-reindent-body (old-depth new-depth &optional number)
3212(defun allout-reindent-body (old-depth new-depth &optional number)
e126900f 3213 "Reindent body lines which were indented at OLD-DEPTH to NEW-DEPTH.
c567ac01
RS
3214
3215Optional arg NUMBER indicates numbering is being added, and it must
a0776d6b 3216be accommodated.
c567ac01
RS
3217
3218Note that refill of indented paragraphs is not done."
1977b8f6
RS
3219
3220 (save-excursion
fd5359c6 3221 (allout-end-of-prefix)
c567ac01
RS
3222 (let* ((new-margin (current-column))
3223 excess old-indent-begin old-indent-end
c567ac01
RS
3224 ;; We want the column where the header-prefix text started
3225 ;; *before* the prefix was changed, so we infer it relative
3226 ;; to the new margin and the shift in depth:
3227 (old-margin (+ old-depth (- new-margin new-depth))))
353e2ef2 3228
c567ac01 3229 ;; Process lines up to (but excluding) next topic header:
fd5359c6 3230 (allout-unprotected
c567ac01
RS
3231 (save-match-data
3232 (while
96b83743 3233 (and (re-search-forward "\n\\(\\s-*\\)"
c567ac01
RS
3234 nil
3235 t)
3236 ;; Register the indent data, before we reset the
353e2ef2 3237 ;; match data with a subsequent `looking-at':
c567ac01
RS
3238 (setq old-indent-begin (match-beginning 1)
3239 old-indent-end (match-end 1))
fd5359c6 3240 (not (looking-at allout-regexp)))
d82979ea
EZ
3241 (if (> 0 (setq excess (- (- old-indent-end old-indent-begin)
3242 old-margin)))
c567ac01
RS
3243 ;; Text starts left of old margin - don't adjust:
3244 nil
3245 ;; Text was hanging at or right of old left margin -
3246 ;; reindent it, preserving its existing indentation
3247 ;; beyond the old margin:
3248 (delete-region old-indent-begin old-indent-end)
d82979ea 3249 (indent-to (+ new-margin excess (current-column))))))))))
fd5359c6
MR
3250;;;_ > allout-rebullet-current-heading (arg)
3251(defun allout-rebullet-current-heading (arg)
9179616f
DL
3252 "Solicit new bullet for current visible heading."
3253 (interactive "p")
3254 (let ((initial-col (current-column))
fd5359c6 3255 (on-bullet (eq (point)(allout-current-bullet-pos)))
9179616f
DL
3256 (backwards (if (< arg 0)
3257 (setq arg (* arg -1)))))
3258 (while (> arg 0)
fd5359c6
MR
3259 (save-excursion (allout-back-to-current-heading)
3260 (allout-end-of-prefix)
3261 (allout-rebullet-heading t ;;; solicit
9179616f
DL
3262 nil ;;; depth
3263 nil ;;; number-control
3264 nil ;;; index
3265 t)) ;;; do-successors
3266 (setq arg (1- arg))
3267 (if (<= arg 0)
3268 nil
3269 (setq initial-col nil) ; Override positioning back to init col
3270 (if (not backwards)
fd5359c6
MR
3271 (allout-next-visible-heading 1)
3272 (allout-goto-prefix)
3273 (allout-next-visible-heading -1))))
9179616f 3274 (message "Done.")
fd5359c6 3275 (cond (on-bullet (goto-char (allout-current-bullet-pos)))
9179616f 3276 (initial-col (move-to-column initial-col)))))
fd5359c6
MR
3277;;;_ > allout-rebullet-heading (&optional solicit ...)
3278(defun allout-rebullet-heading (&optional solicit
1977b8f6
RS
3279 new-depth
3280 number-control
3281 index
3282 do-successors)
3283
c567ac01 3284 "Adjust bullet of current topic prefix.
1977b8f6 3285
d82979ea
EZ
3286All args are optional.
3287
9179616f
DL
3288If SOLICIT is non-nil, then the choice of bullet is solicited from
3289user. If it's a character, then that character is offered as the
3290default, otherwise the one suited to the context \(according to
3291distinction or depth) is offered. If non-nil, then the
3292context-specific bullet is just used.
1977b8f6 3293
d82979ea 3294Second arg DEPTH forces the topic prefix to that depth, regardless
9179616f 3295of the topic's current depth.
1977b8f6 3296
c567ac01 3297Third arg NUMBER-CONTROL can force the prefix to or away from
fd5359c6 3298numbered form. It has effect only if `allout-numbered-bullet' is
c567ac01
RS
3299non-nil and soliciting was not explicitly invoked (via first arg).
3300Its effect, numbering or denumbering, then depends on the setting
d82979ea 3301of the forth arg, INDEX.
1977b8f6 3302
d82979ea 3303If NUMBER-CONTROL is non-nil and forth arg INDEX is nil, then the
c567ac01
RS
3304prefix of the topic is forced to be non-numbered. Null index and
3305non-nil NUMBER-CONTROL forces denumbering. Non-nil INDEX (and
3306non-nil NUMBER-CONTROL) forces a numbered-prefix form. If non-nil
3307INDEX is a number, then that number is used for the numbered
3308prefix. Non-nil and non-number means that the index for the
d82979ea 3309numbered prefix will be derived by allout-make-topic-prefix.
1977b8f6 3310
c567ac01
RS
3311Fifth arg DO-SUCCESSORS t means re-resolve count on succeeding
3312siblings.
1977b8f6 3313
fd5359c6
MR
3314Cf vars `allout-stylish-prefixes', `allout-old-style-prefixes',
3315and `allout-numbered-bullet', which all affect the behavior of
c567ac01 3316this function."
1977b8f6 3317
fd5359c6 3318 (let* ((current-depth (allout-depth))
1977b8f6 3319 (new-depth (or new-depth current-depth))
fd5359c6
MR
3320 (mb allout-recent-prefix-beginning)
3321 (me allout-recent-prefix-end)
1977b8f6 3322 (current-bullet (buffer-substring (- me 1) me))
fd5359c6 3323 (new-prefix (allout-make-topic-prefix current-bullet
1977b8f6
RS
3324 nil
3325 new-depth
3326 solicit
3327 number-control
3328 index)))
3329
c567ac01 3330 ;; Is new one is identical to old?
1977b8f6
RS
3331 (if (and (= current-depth new-depth)
3332 (string= current-bullet
3333 (substring new-prefix (1- (length new-prefix)))))
c567ac01 3334 ;; Nothing to do:
1977b8f6
RS
3335 t
3336
3337 ;; New prefix probably different from old:
c567ac01 3338 ; get rid of old one:
fd5359c6 3339 (allout-unprotected (delete-region mb me))
1977b8f6 3340 (goto-char mb)
c567ac01
RS
3341 ; Dispense with number if
3342 ; numbered-bullet prefix:
fd5359c6
MR
3343 (if (and allout-numbered-bullet
3344 (string= allout-numbered-bullet current-bullet)
1977b8f6 3345 (looking-at "[0-9]+"))
fd5359c6 3346 (allout-unprotected
c567ac01 3347 (delete-region (match-beginning 0)(match-end 0))))
1977b8f6 3348
c567ac01 3349 ; Put in new prefix:
fd5359c6 3350 (allout-unprotected (insert new-prefix))
1977b8f6 3351
d82979ea 3352 ;; Reindent the body if elected, margin changed, and not encrypted body:
fd5359c6 3353 (if (and allout-reindent-bodies
d82979ea
EZ
3354 (not (= new-depth current-depth))
3355 (not (allout-encrypted-topic-p)))
fd5359c6 3356 (allout-reindent-body current-depth new-depth))
1977b8f6 3357
c567ac01
RS
3358 ;; Recursively rectify successive siblings of orig topic if
3359 ;; caller elected for it:
3360 (if do-successors
3361 (save-excursion
fd5359c6 3362 (while (allout-next-sibling new-depth nil)
c567ac01
RS
3363 (setq index
3364 (cond ((numberp index) (1+ index))
fd5359c6
MR
3365 ((not number-control) (allout-sibling-index))))
3366 (if (allout-numbered-type-prefix)
3367 (allout-rebullet-heading nil ;;; solicit
c567ac01
RS
3368 new-depth ;;; new-depth
3369 number-control;;; number-control
3370 index ;;; index
3371 nil))))) ;;;(dont!)do-successors
3372 ) ; (if (and (= current-depth new-depth)...))
fd5359c6 3373 ) ; let* ((current-depth (allout-depth))...)
c567ac01 3374 ) ; defun
fd5359c6
MR
3375;;;_ > allout-rebullet-topic (arg)
3376(defun allout-rebullet-topic (arg)
d82979ea 3377 "Rebullet the visible topic containing point and all contained subtopics.
19b84ba3 3378
c567ac01 3379Descends into invisible as well as visible topics, however.
1977b8f6 3380
c567ac01 3381With repeat count, shift topic depth by that amount."
1977b8f6 3382 (interactive "P")
96b83743 3383 (let ((start-col (current-column)))
1977b8f6
RS
3384 (save-excursion
3385 ;; Normalize arg:
3386 (cond ((null arg) (setq arg 0))
3387 ((listp arg) (setq arg (car arg))))
3388 ;; Fill the user in, in case we're shifting a big topic:
3389 (if (not (zerop arg)) (message "Shifting..."))
fd5359c6
MR
3390 (allout-back-to-current-heading)
3391 (if (<= (+ (allout-recent-depth) arg) 0)
1977b8f6 3392 (error "Attempt to shift topic below level 1"))
fd5359c6 3393 (allout-rebullet-topic-grunt arg)
1977b8f6 3394 (if (not (zerop arg)) (message "Shifting... done.")))
c567ac01 3395 (move-to-column (max 0 (+ start-col arg)))))
fd5359c6
MR
3396;;;_ > allout-rebullet-topic-grunt (&optional relative-depth ...)
3397(defun allout-rebullet-topic-grunt (&optional relative-depth
1977b8f6
RS
3398 starting-depth
3399 starting-point
3400 index
3401 do-successors)
d82979ea
EZ
3402 "Like `allout-rebullet-topic', but on nearest containing topic
3403\(visible or not).
1977b8f6 3404
d82979ea 3405See `allout-rebullet-heading' for rebulleting behavior.
1977b8f6 3406
d82979ea 3407All arguments are optional.
1977b8f6 3408
d82979ea
EZ
3409First arg RELATIVE-DEPTH means to shift the depth of the entire
3410topic that amount.
e126900f 3411
d82979ea
EZ
3412The rest of the args are for internal recursive use by the function
3413itself. The are STARTING-DEPTH, STARTING-POINT, and INDEX."
1977b8f6
RS
3414
3415 (let* ((relative-depth (or relative-depth 0))
fd5359c6 3416 (new-depth (allout-depth))
1977b8f6
RS
3417 (starting-depth (or starting-depth new-depth))
3418 (on-starting-call (null starting-point))
3419 (index (or index
3420 ;; Leave index null on starting call, so rebullet-heading
3421 ;; calculates it at what might be new depth:
3422 (and (or (zerop relative-depth)
3423 (not on-starting-call))
fd5359c6 3424 (allout-sibling-index))))
1977b8f6
RS
3425 (moving-outwards (< 0 relative-depth))
3426 (starting-point (or starting-point (point))))
3427
3428 ;; Sanity check for excessive promotion done only on starting call:
3429 (and on-starting-call
3430 moving-outwards
3431 (> 0 (+ starting-depth relative-depth))
6a05d05f 3432 (error "Attempt to shift topic out beyond level 1")) ;;; ====>
1977b8f6
RS
3433
3434 (cond ((= starting-depth new-depth)
3435 ;; We're at depth to work on this one:
fd5359c6 3436 (allout-rebullet-heading nil ;;; solicit
1977b8f6
RS
3437 (+ starting-depth ;;; starting-depth
3438 relative-depth)
3439 nil ;;; number
3440 index ;;; index
3441 ;; Every contained topic will get hit,
3442 ;; and we have to get to outside ones
3443 ;; deliberately:
3444 nil) ;;; do-successors
3445 ;; ... and work on subsequent ones which are at greater depth:
3446 (setq index 0)
fd5359c6 3447 (allout-next-heading)
1977b8f6 3448 (while (and (not (eobp))
fd5359c6 3449 (< starting-depth (allout-recent-depth)))
1977b8f6 3450 (setq index (1+ index))
fd5359c6 3451 (allout-rebullet-topic-grunt relative-depth ;;; relative-depth
1977b8f6
RS
3452 (1+ starting-depth);;;starting-depth
3453 starting-point ;;; starting-point
3454 index))) ;;; index
3455
3456 ((< starting-depth new-depth)
3457 ;; Rare case - subtopic more than one level deeper than parent.
3458 ;; Treat this one at an even deeper level:
fd5359c6 3459 (allout-rebullet-topic-grunt relative-depth ;;; relative-depth
1977b8f6
RS
3460 new-depth ;;; starting-depth
3461 starting-point ;;; starting-point
3462 index))) ;;; index
3463
3464 (if on-starting-call
3465 (progn
3466 ;; Rectify numbering of former siblings of the adjusted topic,
3467 ;; if topic has changed depth
3468 (if (or do-successors
3469 (and (not (zerop relative-depth))
fd5359c6
MR
3470 (or (= (allout-recent-depth) starting-depth)
3471 (= (allout-recent-depth) (+ starting-depth
1977b8f6 3472 relative-depth)))))
fd5359c6 3473 (allout-rebullet-heading nil nil nil nil t))
1977b8f6
RS
3474 ;; Now rectify numbering of new siblings of the adjusted topic,
3475 ;; if depth has been changed:
3476 (progn (goto-char starting-point)
3477 (if (not (zerop relative-depth))
fd5359c6 3478 (allout-rebullet-heading nil nil nil nil t)))))
1977b8f6
RS
3479 )
3480 )
fd5359c6
MR
3481;;;_ > allout-renumber-to-depth (&optional depth)
3482(defun allout-renumber-to-depth (&optional depth)
19b84ba3
RS
3483 "Renumber siblings at current depth.
3484
3485Affects superior topics if optional arg DEPTH is less than current depth.
c567ac01
RS
3486
3487Returns final depth."
3488
3489 ;; Proceed by level, processing subsequent siblings on each,
3490 ;; ascending until we get shallower than the start depth:
3491
fd5359c6 3492 (let ((ascender (allout-depth))
9179616f 3493 was-eobp)
c567ac01 3494 (while (and (not (eobp))
fd5359c6
MR
3495 (allout-depth)
3496 (>= (allout-recent-depth) depth)
c567ac01
RS
3497 (>= ascender depth))
3498 ; Skip over all topics at
3499 ; lesser depths, which can not
3500 ; have been disturbed:
9179616f 3501 (while (and (not (setq was-eobp (eobp)))
fd5359c6
MR
3502 (> (allout-recent-depth) ascender))
3503 (allout-next-heading))
c567ac01 3504 ; Prime ascender for ascension:
fd5359c6
MR
3505 (setq ascender (1- (allout-recent-depth)))
3506 (if (>= (allout-recent-depth) depth)
3507 (allout-rebullet-heading nil ;;; solicit
c567ac01
RS
3508 nil ;;; depth
3509 nil ;;; number-control
3510 nil ;;; index
9179616f
DL
3511 t)) ;;; do-successors
3512 (if was-eobp (goto-char (point-max)))))
fd5359c6
MR
3513 (allout-recent-depth))
3514;;;_ > allout-number-siblings (&optional denumber)
3515(defun allout-number-siblings (&optional denumber)
c567ac01 3516 "Assign numbered topic prefix to this topic and its siblings.
1977b8f6 3517
c567ac01
RS
3518With universal argument, denumber - assign default bullet to this
3519topic and its siblings.
1977b8f6 3520
c567ac01
RS
3521With repeated universal argument (`^U^U'), solicit bullet for each
3522rebulleting each topic at this level."
1977b8f6
RS
3523
3524 (interactive "P")
3525
3526 (save-excursion
fd5359c6
MR
3527 (allout-back-to-current-heading)
3528 (allout-beginning-of-level)
3529 (let ((depth (allout-recent-depth))
c567ac01 3530 (index (if (not denumber) 1))
1977b8f6
RS
3531 (use-bullet (equal '(16) denumber))
3532 (more t))
3533 (while more
fd5359c6 3534 (allout-rebullet-heading use-bullet ;;; solicit
c567ac01 3535 depth ;;; depth
1977b8f6
RS
3536 t ;;; number-control
3537 index ;;; index
3538 nil) ;;; do-successors
3539 (if index (setq index (1+ index)))
fd5359c6
MR
3540 (setq more (allout-next-sibling depth nil))))))
3541;;;_ > allout-shift-in (arg)
3542(defun allout-shift-in (arg)
d82979ea
EZ
3543 "Increase depth of current heading and any topics collapsed within it.
3544
3545We disallow shifts that would result in the topic having a depth more than
3546one level greater than the immediately previous topic, to avoid containment
3547discontinuity. The first topic in the file can be adjusted to any positive
3548depth, however."
1977b8f6 3549 (interactive "p")
d82979ea
EZ
3550 (if (> arg 0)
3551 (save-excursion
3552 (allout-back-to-current-heading)
3553 (if (not (bobp))
3554 (let* ((current-depth (allout-recent-depth))
3555 (start-point (point))
3556 (predecessor-depth (progn
3557 (forward-char -1)
3558 (allout-goto-prefix)
3559 (if (< (point) start-point)
3560 (allout-recent-depth)
3561 0))))
3562 (if (and (> predecessor-depth 0)
3563 (> (+ current-depth arg)
3564 (1+ predecessor-depth)))
96b83743
EZ
3565 (error (concat "Disallowed shift deeper than"
3566 " containing topic's children.")))))))
fd5359c6
MR
3567 (allout-rebullet-topic arg))
3568;;;_ > allout-shift-out (arg)
3569(defun allout-shift-out (arg)
d82979ea
EZ
3570 "Decrease depth of current heading and any topics collapsed within it.
3571
3572We disallow shifts that would result in the topic having a depth more than
3573one level greater than the immediately previous topic, to avoid containment
3574discontinuity. The first topic in the file can be adjusted to any positive
3575depth, however."
1977b8f6 3576 (interactive "p")
d82979ea
EZ
3577 (if (< arg 0)
3578 (allout-shift-in (* arg -1)))
fd5359c6 3579 (allout-rebullet-topic (* arg -1)))
c567ac01 3580;;;_ : Surgery (kill-ring) functions with special provisions for outlines:
fd5359c6
MR
3581;;;_ > allout-kill-line (&optional arg)
3582(defun allout-kill-line (&optional arg)
c567ac01 3583 "Kill line, adjusting subsequent lines suitably for outline mode."
1977b8f6
RS
3584
3585 (interactive "*P")
d82979ea 3586
96b83743
EZ
3587 (if (or (not (allout-mode-p))
3588 (not (bolp))
3589 (not (looking-at allout-regexp)))
4034b0e2 3590 ;; Just do a regular kill:
96b83743
EZ
3591 (kill-line arg)
3592 ;; Ah, have to watch out for adjustments:
3593 (let* ((beg (point))
3594 (beg-hidden (allout-hidden-p))
3595 (end-hidden (save-excursion (allout-end-of-current-line)
3596 (allout-hidden-p)))
3597 (depth (allout-depth))
3598 (collapsed (allout-current-topic-collapsed-p)))
3599
3600 (if collapsed
3601 (put-text-property beg (1+ beg) 'allout-was-collapsed t)
3602 (remove-text-properties beg (1+ beg) '(allout-was-collapsed t)))
3603
3604 (if (and (not beg-hidden) (not end-hidden))
3605 (allout-unprotected (kill-line arg))
3606 (kill-line arg))
c567ac01 3607 ; Provide some feedback:
96b83743
EZ
3608 (sit-for 0)
3609 (if allout-numbered-bullet
3610 (save-excursion ; Renumber subsequent topics if needed:
d82979ea
EZ
3611 (if (not (looking-at allout-regexp))
3612 (allout-next-heading))
96b83743 3613 (allout-renumber-to-depth depth))))))
fd5359c6
MR
3614;;;_ > allout-kill-topic ()
3615(defun allout-kill-topic ()
c567ac01
RS
3616 "Kill topic together with subtopics.
3617
96b83743
EZ
3618Trailing whitespace is killed with a topic if that whitespace:
3619
3620 - would separate the topic from a subsequent sibling
3621 - would separate the topic from the end of buffer
3622 - would not be added to whitespace already separating the topic from the
3623 previous one.
3624
3625Completely collapsed topics are marked as such, for re-collapse
3626when yank with allout-yank into an outline as a heading."
1977b8f6
RS
3627
3628 ;; Some finagling is done to make complex topic kills appear faster
3629 ;; than they actually are. A redisplay is performed immediately
96b83743 3630 ;; after the region is deleted, though the renumbering process
1977b8f6 3631 ;; has yet to be performed. This means that there may appear to be
96b83743 3632 ;; a lag *after* a kill has been performed.
1977b8f6
RS
3633
3634 (interactive)
0458fa80
EZ
3635 (let* ((inhibit-field-text-motion t)
3636 (collapsed (allout-current-topic-collapsed-p))
96b83743 3637 (beg (prog1 (allout-back-to-current-heading) (beginning-of-line)))
fd5359c6
MR
3638 (depth (allout-recent-depth)))
3639 (allout-end-of-current-subtree)
96b83743
EZ
3640 (if (and (/= (current-column) 0) (not (eobp)))
3641 (forward-char 1))
1977b8f6 3642 (if (not (eobp))
96b83743
EZ
3643 (if (and (looking-at "\n")
3644 (or (save-excursion
3645 (or (not (allout-next-heading))
3646 (= depth (allout-recent-depth))))
3647 (and (> (- beg (point-min)) 3)
3648 (string= (buffer-substring (- beg 2) beg) "\n\n"))))
c567ac01 3649 (forward-char 1)))
353e2ef2 3650
96b83743 3651 (if collapsed
01fc9422
EZ
3652 (allout-unprotected
3653 (put-text-property beg (1+ beg) 'allout-was-collapsed t))
3654 (allout-unprotected
3655 (remove-text-properties beg (1+ beg) '(allout-was-collapsed t))))
d82979ea 3656 (allout-unprotected (kill-region beg (point)))
1977b8f6
RS
3657 (sit-for 0)
3658 (save-excursion
fd5359c6
MR
3659 (allout-renumber-to-depth depth))))
3660;;;_ > allout-yank-processing ()
3661(defun allout-yank-processing (&optional arg)
c567ac01 3662
96b83743 3663 "Incidental allout-specific business to be done just after text yanks.
19b84ba3 3664
c567ac01 3665Does depth adjustment of yanked topics, when:
1977b8f6 3666
c567ac01
RS
36671 the stuff being yanked starts with a valid outline header prefix, and
36682 it is being yanked at the end of a line which consists of only a valid
1977b8f6
RS
3669 topic prefix.
3670
a0776d6b 3671Also, adjusts numbering of subsequent siblings when appropriate.
1977b8f6 3672
c567ac01
RS
3673Depth adjustment alters the depth of all the topics being yanked
3674the amount it takes to make the first topic have the depth of the
3675header into which it's being yanked.
1977b8f6 3676
c567ac01
RS
3677The point is left in front of yanked, adjusted topics, rather than
3678at the end (and vice-versa with the mark). Non-adjusted yanks,
d82979ea 3679however, are left exactly like normal, non-allout-specific yanks."
c567ac01
RS
3680
3681 (interactive "*P")
3682 ; Get to beginning, leaving
3683 ; region around subject:
96b83743 3684 (if (< (allout-mark-marker t) (point))
c567ac01 3685 (exchange-point-and-mark))
0458fa80
EZ
3686 (let* ((inhibit-field-text-motion t)
3687 (subj-beg (point))
96b83743
EZ
3688 (into-bol (bolp))
3689 (subj-end (allout-mark-marker t))
3690 (was-collapsed (get-text-property subj-beg 'allout-was-collapsed))
9179616f 3691 ;; 'resituate' if yanking an entire topic into topic header:
fd5359c6
MR
3692 (resituate (and (allout-e-o-prefix-p)
3693 (looking-at (concat "\\(" allout-regexp "\\)"))
3694 (allout-prefix-data (match-beginning 1)
c567ac01 3695 (match-end 1))))
353e2ef2 3696 ;; `rectify-numbering' if resituating (where several topics may
c567ac01
RS
3697 ;; be resituating) or yanking a topic into a topic slot (bol):
3698 (rectify-numbering (or resituate
96b83743 3699 (and into-bol (looking-at allout-regexp)))))
c567ac01
RS
3700 (if resituate
3701 ; The yanked stuff is a topic:
3702 (let* ((prefix-len (- (match-end 1) subj-beg))
fd5359c6
MR
3703 (subj-depth (allout-recent-depth))
3704 (prefix-bullet (allout-recent-bullet))
c567ac01
RS
3705 (adjust-to-depth
3706 ;; Nil if adjustment unnecessary, otherwise depth to which
3707 ;; adjustment should be made:
3708 (save-excursion
3709 (and (goto-char subj-end)
3710 (eolp)
3711 (goto-char subj-beg)
fd5359c6 3712 (and (looking-at allout-regexp)
c567ac01
RS
3713 (progn
3714 (beginning-of-line)
3715 (not (= (point) subj-beg)))
fd5359c6
MR
3716 (looking-at allout-regexp)
3717 (allout-prefix-data (match-beginning 0)
c567ac01 3718 (match-end 0)))
fd5359c6 3719 (allout-recent-depth))))
c567ac01 3720 (more t))
fd5359c6 3721 (setq rectify-numbering allout-numbered-bullet)
c567ac01
RS
3722 (if adjust-to-depth
3723 ; Do the adjustment:
3724 (progn
3725 (message "... yanking") (sit-for 0)
3726 (save-restriction
3727 (narrow-to-region subj-beg subj-end)
3728 ; Trim off excessive blank
3729 ; line at end, if any:
3730 (goto-char (point-max))
3731 (if (looking-at "^$")
fd5359c6 3732 (allout-unprotected (delete-char -1)))
c567ac01
RS
3733 ; Work backwards, with each
3734 ; shallowest level,
3735 ; successively excluding the
3736 ; last processed topic from
3737 ; the narrow region:
3738 (while more
fd5359c6 3739 (allout-back-to-current-heading)
c567ac01 3740 ; go as high as we can in each bunch:
fd5359c6 3741 (while (allout-ascend-to-depth (1- (allout-depth))))
c567ac01 3742 (save-excursion
fd5359c6 3743 (allout-rebullet-topic-grunt (- adjust-to-depth
c567ac01 3744 subj-depth))
fd5359c6 3745 (allout-depth))
c567ac01
RS
3746 (if (setq more (not (bobp)))
3747 (progn (widen)
3748 (forward-char -1)
3749 (narrow-to-region subj-beg (point))))))
3750 (message "")
3751 ;; Preserve new bullet if it's a distinctive one, otherwise
3752 ;; use old one:
3753 (if (string-match (regexp-quote prefix-bullet)
fd5359c6 3754 allout-distinctive-bullets-string)
c567ac01
RS
3755 ; Delete from bullet of old to
3756 ; before bullet of new:
3757 (progn
3758 (beginning-of-line)
3759 (delete-region (point) subj-beg)
96b83743 3760 (set-marker (allout-mark-marker t) subj-end)
c567ac01 3761 (goto-char subj-beg)
fd5359c6 3762 (allout-end-of-prefix))
c567ac01
RS
3763 ; Delete base subj prefix,
3764 ; leaving old one:
3765 (delete-region (point) (+ (point)
3766 prefix-len
3767 (- adjust-to-depth subj-depth)))
3768 ; and delete residual subj
3769 ; prefix digits and space:
3770 (while (looking-at "[0-9]") (delete-char 1))
3771 (if (looking-at " ") (delete-char 1))))
3772 (exchange-point-and-mark))))
3773 (if rectify-numbering
353e2ef2 3774 (progn
c567ac01
RS
3775 (save-excursion
3776 ; Give some preliminary feedback:
3777 (message "... reconciling numbers") (sit-for 0)
3778 ; ... and renumber, in case necessary:
3779 (goto-char subj-beg)
fd5359c6
MR
3780 (if (allout-goto-prefix)
3781 (allout-rebullet-heading nil ;;; solicit
3782 (allout-depth) ;;; depth
c567ac01
RS
3783 nil ;;; number-control
3784 nil ;;; index
3785 t))
3786 (message ""))))
96b83743
EZ
3787 (when (and (or into-bol resituate) was-collapsed)
3788 (remove-text-properties subj-beg (1+ subj-beg) '(allout-was-collapsed))
3789 (allout-hide-current-subtree))
c567ac01
RS
3790 (if (not resituate)
3791 (exchange-point-and-mark))))
fd5359c6
MR
3792;;;_ > allout-yank (&optional arg)
3793(defun allout-yank (&optional arg)
aad94676 3794 "`allout-mode' yank, with depth and numbering adjustment of yanked topics.
19b84ba3 3795
a0776d6b 3796Non-topic yanks work no differently than normal yanks.
c567ac01
RS
3797
3798If a topic is being yanked into a bare topic prefix, the depth of the
3799yanked topic is adjusted to the depth of the topic prefix.
3800
2265e017 3801 1 we're yanking in an `allout-mode' buffer
c567ac01
RS
3802 2 the stuff being yanked starts with a valid outline header prefix, and
3803 3 it is being yanked at the end of a line which consists of only a valid
3804 topic prefix.
3805
3806If these conditions hold then the depth of the yanked topics are all
3807adjusted the amount it takes to make the first one at the depth of the
3808header into which it's being yanked.
3809
3810The point is left in front of yanked, adjusted topics, rather than
3811at the end (and vice-versa with the mark). Non-adjusted yanks,
3812however, (ones that don't qualify for adjustment) are handled
3813exactly like normal yanks.
3814
a0776d6b 3815Numbering of yanked topics, and the successive siblings at the depth
c567ac01
RS
3816into which they're being yanked, is adjusted.
3817
539d7736
JB
3818`allout-yank-pop' works with `allout-yank' just like normal `yank-pop'
3819works with normal `yank' in non-outline buffers."
1977b8f6
RS
3820
3821 (interactive "*P")
3822 (setq this-command 'yank)
c567ac01 3823 (yank arg)
fd5359c6 3824 (if (allout-mode-p)
96b83743
EZ
3825 (allout-yank-processing))
3826)
fd5359c6
MR
3827;;;_ > allout-yank-pop (&optional arg)
3828(defun allout-yank-pop (&optional arg)
2265e017 3829 "Yank-pop like `allout-yank' when popping to bare outline prefixes.
19b84ba3
RS
3830
3831Adapts level of popped topics to level of fresh prefix.
c567ac01
RS
3832
3833Note - prefix changes to distinctive bullets will stick, if followed
3834by pops to non-distinctive yanks. Bug..."
1977b8f6
RS
3835
3836 (interactive "*p")
1977b8f6 3837 (setq this-command 'yank)
c567ac01 3838 (yank-pop arg)
fd5359c6
MR
3839 (if (allout-mode-p)
3840 (allout-yank-processing)))
1977b8f6 3841
c567ac01
RS
3842;;;_ - Specialty bullet functions
3843;;;_ : File Cross references
fd5359c6
MR
3844;;;_ > allout-resolve-xref ()
3845(defun allout-resolve-xref ()
19b84ba3
RS
3846 "Pop to file associated with current heading, if it has an xref bullet.
3847
fd5359c6 3848\(Works according to setting of `allout-file-xref-bullet')."
1977b8f6 3849 (interactive)
fd5359c6 3850 (if (not allout-file-xref-bullet)
1977b8f6 3851 (error
635ca796 3852 "Outline cross references disabled - no `allout-file-xref-bullet'")
fd5359c6 3853 (if (not (string= (allout-current-bullet) allout-file-xref-bullet))
635ca796 3854 (error "Current heading lacks cross-reference bullet `%s'"
fd5359c6 3855 allout-file-xref-bullet)
0458fa80
EZ
3856 (let ((inhibit-field-text-motion t)
3857 file-name)
1977b8f6 3858 (save-excursion
fd5359c6 3859 (let* ((text-start allout-recent-prefix-end)
fa29cfef 3860 (heading-end (progn (end-of-line) (point))))
1977b8f6
RS
3861 (goto-char text-start)
3862 (setq file-name
3863 (if (re-search-forward "\\s-\\(\\S-*\\)" heading-end t)
3864 (buffer-substring (match-beginning 1) (match-end 1))))))
69027dea 3865 (setq file-name (expand-file-name file-name))
1977b8f6
RS
3866 (if (or (file-exists-p file-name)
3867 (if (file-writable-p file-name)
3868 (y-or-n-p (format "%s not there, create one? "
3869 file-name))
3870 (error "%s not found and can't be created" file-name)))
3871 (condition-case failure
3872 (find-file-other-window file-name)
9179616f 3873 ('error failure))
1977b8f6
RS
3874 (error "%s not found" file-name))
3875 )
3876 )
3877 )
3878 )
19b84ba3 3879
9179616f 3880;;;_ #6 Exposure Control
c567ac01
RS
3881
3882;;;_ - Fundamental
d82979ea
EZ
3883;;;_ > allout-flag-region (from to flag)
3884(defun allout-flag-region (from to flag)
96b83743
EZ
3885 "Conceal text from FROM to TO if FLAG is non-nil, else reveal it.
3886
3887Text is shown if flag is nil and hidden otherwise."
3888 ;; We use outline invisibility spec.
01fc9422 3889 (remove-overlays from to 'category 'allout-exposure-category)
96b83743
EZ
3890 (when flag
3891 (let ((o (make-overlay from to)))
01fc9422 3892 (overlay-put o 'category 'allout-exposure-category)
96b83743 3893 (when (featurep 'xemacs)
01fc9422 3894 (let ((props (symbol-plist 'allout-exposure-category)))
96b83743
EZ
3895 (while props
3896 (overlay-put o (pop props) (pop props)))))))
da506f05
RS
3897 (run-hooks 'allout-view-change-hook)
3898 (run-hooks 'allout-exposure-change-hook))
fd5359c6
MR
3899;;;_ > allout-flag-current-subtree (flag)
3900(defun allout-flag-current-subtree (flag)
96b83743 3901 "Conceal currently-visible topic's subtree if FLAG non-nil, else reveal it."
19b84ba3 3902
c567ac01 3903 (save-excursion
fd5359c6 3904 (allout-back-to-current-heading)
0458fa80
EZ
3905 (let ((inhibit-field-text-motion t))
3906 (end-of-line))
96b83743
EZ
3907 (allout-flag-region (point)
3908 ;; Exposing must not leave trailing blanks hidden,
3909 ;; but can leave them exposed when hiding, so we
3910 ;; can use flag's inverse as the
3911 ;; include-trailing-blank cue:
3912 (allout-end-of-current-subtree (not flag))
3913 flag)))
c567ac01 3914
c567ac01 3915;;;_ - Topic-specific
01fc9422
EZ
3916;;;_ > allout-show-entry ()
3917(defun allout-show-entry ()
3918 "Like `allout-show-current-entry', but reveals entries in hidden topics.
c567ac01
RS
3919
3920This is a way to give restricted peek at a concealed locality without the
3921expense of exposing its context, but can leave the outline with aberrant
96b83743
EZ
3922exposure. `allout-show-offshoot' should be used after the peek to rectify
3923the exposure."
c567ac01
RS
3924
3925 (interactive)
3926 (save-excursion
96b83743 3927 (let (beg end)
fd5359c6 3928 (allout-goto-prefix)
96b83743
EZ
3929 (setq beg (if (allout-hidden-p) (1- (point)) (point)))
3930 (setq end (allout-pre-next-prefix))
3931 (allout-flag-region beg end nil)
9179616f 3932 (list beg end))))
fd5359c6
MR
3933;;;_ > allout-show-children (&optional level strict)
3934(defun allout-show-children (&optional level strict)
c567ac01
RS
3935
3936 "If point is visible, show all direct subheadings of this heading.
19b84ba3 3937
2265e017 3938Otherwise, do `allout-show-to-offshoot', and then show subheadings.
c567ac01
RS
3939
3940Optional LEVEL specifies how many levels below the current level
3941should be shown, or all levels if t. Default is 1.
3942
3943Optional STRICT means don't resort to -show-to-offshoot, no matter
3944what. This is basically so -show-to-offshoot, which is called by
3945this function, can employ the pure offspring-revealing capabilities of
19b84ba3
RS
3946it.
3947
3948Returns point at end of subtree that was opened, if any. (May get a
3949point of non-opened subtree?)"
c567ac01
RS
3950
3951 (interactive "p")
96b83743 3952 (let ((start-point (point)))
19b84ba3 3953 (if (and (not strict)
96b83743 3954 (allout-hidden-p))
c567ac01 3955
96b83743
EZ
3956 (progn (allout-show-to-offshoot) ; Point's concealed, open to
3957 ; expose it.
3958 ;; Then recurse, but with "strict" set so we don't
3959 ;; infinite regress:
3960 (allout-show-children level t))
c567ac01 3961
19b84ba3 3962 (save-excursion
96b83743
EZ
3963 (allout-beginning-of-current-line)
3964 (save-restriction
3965 (let* ((chart (allout-chart-subtree (or level 1)))
3966 (to-reveal (allout-chart-to-reveal chart (or level 1))))
3967 (goto-char start-point)
3968 (when (and strict (allout-hidden-p))
3969 ;; Concealed root would already have been taken care of,
3970 ;; unless strict was set.
3971 (allout-flag-region (point) (allout-snug-back) nil)
3972 (when allout-show-bodies
3973 (goto-char (car to-reveal))
3974 (allout-show-current-entry)))
3975 (while to-reveal
3976 (goto-char (car to-reveal))
3977 (allout-flag-region (save-excursion (allout-snug-back) (point))
3978 (progn (search-forward "\n" nil t)
3979 (1- (point)))
3980 nil)
3981 (when allout-show-bodies
3982 (goto-char (car to-reveal))
3983 (allout-show-current-entry))
3984 (setq to-reveal (cdr to-reveal)))))))
3985 ;; Compensate for `save-excursion's maintenance of point
3986 ;; within invisible text:
3987 (goto-char start-point)))
fd5359c6
MR
3988;;;_ > allout-show-to-offshoot ()
3989(defun allout-show-to-offshoot ()
2265e017 3990 "Like `allout-show-entry', but reveals all concealed ancestors, as well.
c567ac01 3991
96b83743 3992Useful for coherently exposing to a random point in a hidden region."
c567ac01
RS
3993 (interactive)
3994 (save-excursion
0458fa80
EZ
3995 (let ((inhibit-field-text-motion t)
3996 (orig-pt (point))
fd5359c6 3997 (orig-pref (allout-goto-prefix))
c567ac01
RS
3998 (last-at (point))
3999 bag-it)
96b83743
EZ
4000 (while (or bag-it (allout-hidden-p))
4001 (while (allout-hidden-p)
4002 ;; XXX We would use `(move-beginning-of-line 1)', but it gets
4003 ;; stuck on hidden newlines at column 80, as of GNU Emacs 22.0.50.
4004 (beginning-of-line)
4005 (if (allout-hidden-p) (forward-char -1)))
c567ac01
RS
4006 (if (= last-at (setq last-at (point)))
4007 ;; Oops, we're not making any progress! Show the current
4008 ;; topic completely, and bag this try.
4009 (progn (beginning-of-line)
fd5359c6 4010 (allout-show-current-subtree)
c567ac01
RS
4011 (goto-char orig-pt)
4012 (setq bag-it t)
4013 (beep)
4014 (message "%s: %s"
fd5359c6 4015 "allout-show-to-offshoot: "
c567ac01 4016 "Aberrant nesting encountered.")))
fd5359c6 4017 (allout-show-children)
c567ac01 4018 (goto-char orig-pref))
19b84ba3 4019 (goto-char orig-pt)))
fd5359c6
MR
4020 (if (allout-hidden-p)
4021 (allout-show-entry)))
4022;;;_ > allout-hide-current-entry ()
4023(defun allout-hide-current-entry ()
c567ac01
RS
4024 "Hide the body directly following this heading."
4025 (interactive)
fd5359c6 4026 (allout-back-to-current-heading)
c567ac01 4027 (save-excursion
0458fa80
EZ
4028 (let ((inhibit-field-text-motion t))
4029 (end-of-line))
96b83743 4030 (allout-flag-region (point)
d82979ea 4031 (progn (allout-end-of-entry) (point))
96b83743 4032 t)))
fd5359c6
MR
4033;;;_ > allout-show-current-entry (&optional arg)
4034(defun allout-show-current-entry (&optional arg)
96b83743 4035 "Show body following current heading, or hide entry with universal argument."
c567ac01
RS
4036
4037 (interactive "P")
4038 (if arg
fd5359c6 4039 (allout-hide-current-entry)
96b83743 4040 (save-excursion (allout-show-to-offshoot))
c567ac01 4041 (save-excursion
fd5359c6 4042 (allout-flag-region (point)
96b83743
EZ
4043 (progn (allout-end-of-entry t) (point))
4044 nil)
d82979ea 4045 )))
fd5359c6
MR
4046;;;_ > allout-show-current-subtree (&optional arg)
4047(defun allout-show-current-subtree (&optional arg)
19b84ba3 4048 "Show everything within the current topic. With a repeat-count,
353e2ef2 4049expose this topic and its siblings."
19b84ba3
RS
4050 (interactive "P")
4051 (save-excursion
fd5359c6 4052 (if (<= (allout-current-depth) 0)
19b84ba3 4053 ;; Outside any topics - try to get to the first:
fd5359c6 4054 (if (not (allout-next-heading))
6a05d05f 4055 (error "No topics")
19b84ba3
RS
4056 ;; got to first, outermost topic - set to expose it and siblings:
4057 (message "Above outermost topic - exposing all.")
96b83743
EZ
4058 (allout-flag-region (point-min)(point-max) nil))
4059 (allout-beginning-of-current-line)
19b84ba3 4060 (if (not arg)
96b83743 4061 (allout-flag-current-subtree nil)
fd5359c6
MR
4062 (allout-beginning-of-level)
4063 (allout-expose-topic '(* :))))))
96b83743
EZ
4064;;;_ > allout-current-topic-collapsed-p (&optional include-single-liners)
4065(defun allout-current-topic-collapsed-p (&optional include-single-liners)
4066 "True if the currently visible containing topic is already collapsed.
4067
64686e6d
RS
4068Single line topics intrinsically can be considered as being both
4069collapsed and uncollapsed. If optional INCLUDE-SINGLE-LINERS is
4070true, then single-line topics are considered to be collapsed. By
4071default, they are treated as being uncollapsed."
96b83743 4072 (save-excursion
64686e6d
RS
4073 (and
4074 (= (progn (allout-back-to-current-heading)
4075 (move-end-of-line 1)
4076 (point))
4077 (allout-end-of-current-subtree (not (looking-at "\n\n"))))
4078 (or include-single-liners
4079 (progn (backward-char 1) (allout-hidden-p))))))
fd5359c6
MR
4080;;;_ > allout-hide-current-subtree (&optional just-close)
4081(defun allout-hide-current-subtree (&optional just-close)
19b84ba3 4082 "Close the current topic, or containing topic if this one is already closed.
c567ac01 4083
19b84ba3 4084If this topic is closed and it's a top level topic, close this topic
353e2ef2 4085and its siblings.
19b84ba3 4086
96b83743 4087If optional arg JUST-CLOSE is non-nil, do not close the parent or
19b84ba3 4088siblings, even if the target topic is already closed."
c567ac01
RS
4089
4090 (interactive)
96b83743
EZ
4091 (let* ((from (point))
4092 (sibs-msg "Top-level topic already closed - closing siblings...")
4093 (current-exposed (not (allout-current-topic-collapsed-p t))))
4094 (cond (current-exposed (allout-flag-current-subtree t))
4095 (just-close nil)
4096 ((allout-up-current-level 1 t) (allout-hide-current-subtree))
4097 (t (goto-char 0)
4098 (message sibs-msg)
5204800f 4099 (allout-goto-prefix)
96b83743
EZ
4100 (allout-expose-topic '(0 :))
4101 (message (concat sibs-msg " Done."))))
4102 (goto-char from)))
fd5359c6
MR
4103;;;_ > allout-show-current-branches ()
4104(defun allout-show-current-branches ()
c567ac01
RS
4105 "Show all subheadings of this heading, but not their bodies."
4106 (interactive)
0458fa80
EZ
4107 (let ((inhibit-field-text-motion t))
4108 (beginning-of-line))
fd5359c6
MR
4109 (allout-show-children t))
4110;;;_ > allout-hide-current-leaves ()
4111(defun allout-hide-current-leaves ()
353e2ef2 4112 "Hide the bodies of the current topic and all its offspring."
c567ac01 4113 (interactive)
fd5359c6
MR
4114 (allout-back-to-current-heading)
4115 (allout-hide-region-body (point) (progn (allout-end-of-current-subtree)
c567ac01
RS
4116 (point))))
4117
4118;;;_ - Region and beyond
fd5359c6
MR
4119;;;_ > allout-show-all ()
4120(defun allout-show-all ()
c567ac01
RS
4121 "Show all of the text in the buffer."
4122 (interactive)
19b84ba3 4123 (message "Exposing entire buffer...")
96b83743 4124 (allout-flag-region (point-min) (point-max) nil)
19b84ba3 4125 (message "Exposing entire buffer... Done."))
fd5359c6
MR
4126;;;_ > allout-hide-bodies ()
4127(defun allout-hide-bodies ()
c567ac01
RS
4128 "Hide all of buffer except headings."
4129 (interactive)
fd5359c6
MR
4130 (allout-hide-region-body (point-min) (point-max)))
4131;;;_ > allout-hide-region-body (start end)
4132(defun allout-hide-region-body (start end)
c567ac01
RS
4133 "Hide all body lines in the region, but not headings."
4134 (save-excursion
4135 (save-restriction
4136 (narrow-to-region start end)
4137 (goto-char (point-min))
0458fa80
EZ
4138 (let ((inhibit-field-text-motion t))
4139 (while (not (eobp))
4140 (end-of-line)
4141 (allout-flag-region (point) (allout-end-of-entry) t)
4142 (if (not (eobp))
4143 (forward-char
4144 (if (looking-at "\n\n")
4145 2 1))))))))
c567ac01 4146
fd5359c6
MR
4147;;;_ > allout-expose-topic (spec)
4148(defun allout-expose-topic (spec)
19b84ba3 4149 "Apply exposure specs to successive outline topic items.
c567ac01 4150
fd5359c6
MR
4151Use the more convenient frontend, `allout-new-exposure', if you don't
4152need evaluation of the arguments, or even better, the `allout-layout'
19b84ba3
RS
4153variable-keyed mode-activation/auto-exposure feature of allout outline
4154mode. See the respective documentation strings for more details.
c567ac01
RS
4155
4156Cursor is left at start position.
4157
19b84ba3
RS
4158SPEC is either a number or a list.
4159
4160Successive specs on a list are applied to successive sibling topics.
c567ac01
RS
4161
4162A simple spec \(either a number, one of a few symbols, or the null
19b84ba3
RS
4163list) dictates the exposure for the corresponding topic.
4164
4165Non-null lists recursively designate exposure specs for respective
4166subtopics of the current topic.
c567ac01 4167
353e2ef2 4168The `:' repeat spec is used to specify exposure for any number of
19b84ba3 4169successive siblings, up to the trailing ones for which there are
353e2ef2 4170explicit specs following the `:'.
c567ac01
RS
4171
4172Simple (numeric and null-list) specs are interpreted as follows:
4173
19b84ba3
RS
4174 Numbers indicate the relative depth to open the corresponding topic.
4175 - negative numbers force the topic to be closed before opening to the
4176 absolute value of the number, so all siblings are open only to
4177 that level.
4178 - positive numbers open to the relative depth indicated by the
4179 number, but do not force already opened subtopics to be closed.
4180 - 0 means to close topic - hide all offspring.
353e2ef2 4181 : - `repeat'
c567ac01 4182 apply prior element to all siblings at current level, *up to*
353e2ef2 4183 those siblings that would be covered by specs following the `:'
c567ac01
RS
4184 on the list. Ie, apply to all topics at level but the last
4185 ones. \(Only first of multiple colons at same level is
4186 respected - subsequent ones are discarded.)
19b84ba3
RS
4187 * - completely opens the topic, including bodies.
4188 + - shows all the sub headers, but not the bodies
4189 - - exposes the body of the corresponding topic.
c567ac01
RS
4190
4191Examples:
fd5359c6 4192\(allout-expose-topic '(-1 : 0))
c567ac01
RS
4193 Close this and all following topics at current level, exposing
4194 only their immediate children, but close down the last topic
4195 at this current level completely.
fd5359c6 4196\(allout-expose-topic '(-1 () : 1 0))
c567ac01
RS
4197 Close current topic so only the immediate subtopics are shown;
4198 show the children in the second to last topic, and completely
4199 close the last one.
fd5359c6 4200\(allout-expose-topic '(-2 : -1 *))
c567ac01
RS
4201 Expose children and grandchildren of all topics at current
4202 level except the last two; expose children of the second to
4203 last and completely open the last one."
4204
4205 (interactive "xExposure spec: ")
19b84ba3
RS
4206 (if (not (listp spec))
4207 nil
fd5359c6 4208 (let ((depth (allout-depth))
19b84ba3
RS
4209 (max-pos 0)
4210 prev-elem curr-elem
96b83743 4211 stay)
19b84ba3
RS
4212 (while spec
4213 (setq prev-elem curr-elem
4214 curr-elem (car spec)
4215 spec (cdr spec))
4216 (cond ; Do current element:
4217 ((null curr-elem) nil)
4218 ((symbolp curr-elem)
fd5359c6
MR
4219 (cond ((eq curr-elem '*) (allout-show-current-subtree)
4220 (if (> allout-recent-end-of-subtree max-pos)
4221 (setq max-pos allout-recent-end-of-subtree)))
4222 ((eq curr-elem '+) (allout-show-current-branches)
4223 (if (> allout-recent-end-of-subtree max-pos)
4224 (setq max-pos allout-recent-end-of-subtree)))
4225 ((eq curr-elem '-) (allout-show-current-entry))
19b84ba3
RS
4226 ((eq curr-elem ':)
4227 (setq stay t)
353e2ef2 4228 ;; Expand the `repeat' spec to an explicit version,
19b84ba3
RS
4229 ;; w.r.t. remaining siblings:
4230 (let ((residue ; = # of sibs not covered by remaining spec
4231 ;; Dang - could be nice to make use of the chart, sigh:
fd5359c6 4232 (- (length (allout-chart-siblings))
19b84ba3
RS
4233 (length spec))))
4234 (if (< 0 residue)
4235 ;; Some residue - cover it with prev-elem:
4236 (setq spec (append (make-list residue prev-elem)
4237 spec)))))))
4238 ((numberp curr-elem)
96b83743 4239 (if (and (>= 0 curr-elem) (not (allout-hidden-p)))
fd5359c6 4240 (save-excursion (allout-hide-current-subtree t)
19b84ba3
RS
4241 (if (> 0 curr-elem)
4242 nil
fd5359c6 4243 (if (> allout-recent-end-of-subtree max-pos)
19b84ba3 4244 (setq max-pos
fd5359c6 4245 allout-recent-end-of-subtree)))))
19b84ba3 4246 (if (> (abs curr-elem) 0)
fd5359c6
MR
4247 (progn (allout-show-children (abs curr-elem))
4248 (if (> allout-recent-end-of-subtree max-pos)
4249 (setq max-pos allout-recent-end-of-subtree)))))
19b84ba3 4250 ((listp curr-elem)
fd5359c6
MR
4251 (if (allout-descend-to-depth (1+ depth))
4252 (let ((got (allout-expose-topic curr-elem)))
19b84ba3
RS
4253 (if (and got (> got max-pos)) (setq max-pos got))))))
4254 (cond (stay (setq stay nil))
4255 ((listp (car spec)) nil)
4256 ((> max-pos (point))
4257 ;; Capitalize on max-pos state to get us nearer next sibling:
4258 (progn (goto-char (min (point-max) max-pos))
fd5359c6
MR
4259 (allout-next-heading)))
4260 ((allout-next-sibling depth))))
19b84ba3 4261 max-pos)))
fd5359c6
MR
4262;;;_ > allout-old-expose-topic (spec &rest followers)
4263(defun allout-old-expose-topic (spec &rest followers)
d82979ea
EZ
4264
4265 "Deprecated. Use `allout-expose-topic' \(with different schema
4266format) instead.
4267
4268Dictate wholesale exposure scheme for current topic, according to SPEC.
c567ac01
RS
4269
4270SPEC is either a number or a list. Optional successive args
4271dictate exposure for subsequent siblings of current topic.
4272
4273A simple spec (either a number, a special symbol, or the null list)
4274dictates the overall exposure for a topic. Non null lists are
4275composite specs whose first element dictates the overall exposure for
4276a topic, with the subsequent elements in the list interpreted as specs
4277that dictate the exposure for the successive offspring of the topic.
4278
4279Simple (numeric and null-list) specs are interpreted as follows:
4280
4281 - Numbers indicate the relative depth to open the corresponding topic:
4282 - negative numbers force the topic to be close before opening to the
4283 absolute value of the number.
4284 - positive numbers just open to the relative depth indicated by the number.
4285 - 0 just closes
353e2ef2
KH
4286 - `*' completely opens the topic, including bodies.
4287 - `+' shows all the sub headers, but not the bodies
4288 - `-' exposes the body and immediate offspring of the corresponding topic.
c567ac01
RS
4289
4290If the spec is a list, the first element must be a number, which
4291dictates the exposure depth of the topic as a whole. Subsequent
4292elements of the list are nested SPECs, dictating the specific exposure
4293for the corresponding offspring of the topic.
4294
e126900f 4295Optional FOLLOWERS arguments dictate exposure for succeeding siblings."
c567ac01
RS
4296
4297 (interactive "xExposure spec: ")
0458fa80
EZ
4298 (let ((inhibit-field-text-motion t)
4299 (depth (allout-current-depth))
c567ac01
RS
4300 max-pos)
4301 (cond ((null spec) nil)
4302 ((symbolp spec)
fd5359c6
MR
4303 (if (eq spec '*) (allout-show-current-subtree))
4304 (if (eq spec '+) (allout-show-current-branches))
4305 (if (eq spec '-) (allout-show-current-entry)))
c567ac01
RS
4306 ((numberp spec)
4307 (if (>= 0 spec)
fd5359c6 4308 (save-excursion (allout-hide-current-subtree t)
c567ac01
RS
4309 (end-of-line)
4310 (if (or (not max-pos)
4311 (> (point) max-pos))
4312 (setq max-pos (point)))
4313 (if (> 0 spec)
4314 (setq spec (* -1 spec)))))
4315 (if (> spec 0)
fd5359c6 4316 (allout-show-children spec)))
c567ac01 4317 ((listp spec)
fd5359c6 4318 ;(let ((got (allout-old-expose-topic (car spec))))
c567ac01
RS
4319 ; (if (and got (or (not max-pos) (> got max-pos)))
4320 ; (setq max-pos got)))
fd5359c6 4321 (let ((new-depth (+ (allout-current-depth) 1))
c567ac01 4322 got)
fd5359c6 4323 (setq max-pos (allout-old-expose-topic (car spec)))
c567ac01
RS
4324 (setq spec (cdr spec))
4325 (if (and spec
fd5359c6
MR
4326 (allout-descend-to-depth new-depth)
4327 (not (allout-hidden-p)))
4328 (progn (setq got (apply 'allout-old-expose-topic spec))
c567ac01
RS
4329 (if (and got (or (not max-pos) (> got max-pos)))
4330 (setq max-pos got)))))))
4331 (while (and followers
4332 (progn (if (and max-pos (< (point) max-pos))
4333 (progn (goto-char max-pos)
4334 (setq max-pos nil)))
4335 (end-of-line)
fd5359c6
MR
4336 (allout-next-sibling depth)))
4337 (allout-old-expose-topic (car followers))
c567ac01
RS
4338 (setq followers (cdr followers)))
4339 max-pos))
fd5359c6
MR
4340;;;_ > allout-new-exposure '()
4341(defmacro allout-new-exposure (&rest spec)
4342 "Literal frontend for `allout-expose-topic', doesn't evaluate arguments.
2265e017
MR
4343Some arguments that would need to be quoted in `allout-expose-topic'
4344need not be quoted in `allout-new-exposure'.
c567ac01
RS
4345
4346Cursor is left at start position.
4347
d82979ea
EZ
4348Use this instead of obsolete `allout-exposure'.
4349
c567ac01 4350Examples:
bff05d9e 4351\(allout-new-exposure (-1 () () () 1) 0)
c567ac01
RS
4352 Close current topic at current level so only the immediate
4353 subtopics are shown, except also show the children of the
4354 third subtopic; and close the next topic at the current level.
bff05d9e 4355\(allout-new-exposure : -1 0)
c567ac01
RS
4356 Close all topics at current level to expose only their
4357 immediate children, except for the last topic at the current
353e2ef2 4358 level, in which even its immediate children are hidden.
bff05d9e 4359\(allout-new-exposure -2 : -1 *)
c567ac01
RS
4360 Expose children and grandchildren of first topic at current
4361 level, and expose children of subsequent topics at current
4362 level *except* for the last, which should be opened completely."
4363 (list 'save-excursion
fd5359c6
MR
4364 '(if (not (or (allout-goto-prefix)
4365 (allout-next-heading)))
4366 (error "allout-new-exposure: Can't find any outline topics"))
4367 (list 'allout-expose-topic (list 'quote spec))))
c567ac01 4368
9179616f 4369;;;_ #7 Systematic outline presentation - copying, printing, flattening
19b84ba3 4370
9179616f
DL
4371;;;_ - Mapping and processing of topics
4372;;;_ ( See also Subtree Charting, in Navigation code.)
fd5359c6
MR
4373;;;_ > allout-stringify-flat-index (flat-index)
4374(defun allout-stringify-flat-index (flat-index &optional context)
9179616f
DL
4375 "Convert list representing section/subsection/... to document string.
4376
4377Optional arg CONTEXT indicates interior levels to include."
4378 (let ((delim ".")
71296446 4379 result
9179616f
DL
4380 numstr
4381 (context-depth (or (and context 2) 1)))
4382 ;; Take care of the explicit context:
4383 (while (> context-depth 0)
4384 (setq numstr (int-to-string (car flat-index))
4385 flat-index (cdr flat-index)
4386 result (if flat-index
4387 (cons delim (cons numstr result))
4388 (cons numstr result))
4389 context-depth (if flat-index (1- context-depth) 0)))
4390 (setq delim " ")
4391 ;; Take care of the indentation:
4392 (if flat-index
4393 (progn
4394 (while flat-index
4395 (setq result
4396 (cons delim
4397 (cons (make-string
4398 (1+ (truncate (if (zerop (car flat-index))
4399 1
4400 (log10 (car flat-index)))))
4401 ? )
4402 result)))
4403 (setq flat-index (cdr flat-index)))
4404 ;; Dispose of single extra delim:
4405 (setq result (cdr result))))
4406 (apply 'concat result)))
fd5359c6
MR
4407;;;_ > allout-stringify-flat-index-plain (flat-index)
4408(defun allout-stringify-flat-index-plain (flat-index)
9179616f
DL
4409 "Convert list representing section/subsection/... to document string."
4410 (let ((delim ".")
4411 result)
4412 (while flat-index
4413 (setq result (cons (int-to-string (car flat-index))
4414 (if result
4415 (cons delim result))))
4416 (setq flat-index (cdr flat-index)))
4417 (apply 'concat result)))
fd5359c6
MR
4418;;;_ > allout-stringify-flat-index-indented (flat-index)
4419(defun allout-stringify-flat-index-indented (flat-index)
9179616f
DL
4420 "Convert list representing section/subsection/... to document string."
4421 (let ((delim ".")
71296446 4422 result
9179616f
DL
4423 numstr)
4424 ;; Take care of the explicit context:
4425 (setq numstr (int-to-string (car flat-index))
4426 flat-index (cdr flat-index)
4427 result (if flat-index
4428 (cons delim (cons numstr result))
4429 (cons numstr result)))
4430 (setq delim " ")
4431 ;; Take care of the indentation:
4432 (if flat-index
4433 (progn
4434 (while flat-index
4435 (setq result
4436 (cons delim
4437 (cons (make-string
4438 (1+ (truncate (if (zerop (car flat-index))
4439 1
4440 (log10 (car flat-index)))))
4441 ? )
4442 result)))
4443 (setq flat-index (cdr flat-index)))
4444 ;; Dispose of single extra delim:
4445 (setq result (cdr result))))
4446 (apply 'concat result)))
fd5359c6
MR
4447;;;_ > allout-listify-exposed (&optional start end format)
4448(defun allout-listify-exposed (&optional start end format)
19b84ba3 4449
9179616f 4450 "Produce a list representing exposed topics in current region.
c567ac01 4451
fd5359c6 4452This list can then be used by `allout-process-exposed' to manipulate
9179616f 4453the subject region.
353e2ef2 4454
9179616f 4455Optional START and END indicate bounds of region.
19b84ba3 4456
9179616f
DL
4457optional arg, FORMAT, designates an alternate presentation form for
4458the prefix:
c567ac01 4459
9179616f
DL
4460 list - Present prefix as numeric section.subsection..., starting with
4461 section indicated by the list, innermost nesting first.
4462 `indent' \(symbol) - Convert header prefixes to all white space,
4463 except for distinctive bullets.
c567ac01 4464
9179616f
DL
4465The elements of the list produced are lists that represents a topic
4466header and body. The elements of that list are:
c567ac01 4467
9179616f
DL
4468 - a number representing the depth of the topic,
4469 - a string representing the header-prefix, including trailing whitespace and
4470 bullet.
4471 - a string representing the bullet character,
4472 - and a series of strings, each containing one line of the exposed
4473 portion of the topic entry."
c567ac01 4474
9179616f
DL
4475 (interactive "r")
4476 (save-excursion
4477 (let*
0458fa80
EZ
4478 ((inhibit-field-text-motion t)
4479 ;; state vars:
4480 strings prefix result depth new-depth out gone-out bullet beg
9179616f 4481 next done)
c567ac01 4482
9179616f
DL
4483 (goto-char start)
4484 (beginning-of-line)
4485 ;; Goto initial topic, and register preceeding stuff, if any:
fd5359c6 4486 (if (> (allout-goto-prefix) start)
9179616f
DL
4487 ;; First topic follows beginning point - register preliminary stuff:
4488 (setq result (list (list 0 "" nil
4489 (buffer-substring start (1- (point)))))))
4490 (while (and (not done)
4491 (not (eobp)) ; Loop until we've covered the region.
4492 (not (> (point) end)))
fd5359c6
MR
4493 (setq depth (allout-recent-depth) ; Current topics depth,
4494 bullet (allout-recent-bullet) ; ... bullet,
4495 prefix (allout-recent-prefix)
4496 beg (progn (allout-end-of-prefix t) (point))) ; and beginning.
9179616f 4497 (setq done ; The boundary for the current topic:
fd5359c6
MR
4498 (not (allout-next-visible-heading 1)))
4499 (setq new-depth (allout-recent-depth))
9179616f
DL
4500 (setq gone-out out
4501 out (< new-depth depth))
4502 (beginning-of-line)
4503 (setq next (point))
4504 (goto-char beg)
4505 (setq strings nil)
4506 (while (> next (point)) ; Get all the exposed text in
4507 (setq strings
4508 (cons (buffer-substring
4509 beg
4510 ;To hidden text or end of line:
4511 (progn
96b83743
EZ
4512 (end-of-line)
4513 (allout-back-to-visible-text)))
9179616f 4514 strings))
96b83743
EZ
4515 (when (< (point) next) ; Resume from after hid text, if any.
4516 (line-move 1))
9179616f
DL
4517 (setq beg (point)))
4518 ;; Accumulate list for this topic:
4519 (setq strings (nreverse strings))
4520 (setq result
4521 (cons
4522 (if format
4523 (let ((special (if (string-match
4524 (regexp-quote bullet)
fd5359c6 4525 allout-distinctive-bullets-string)
9179616f
DL
4526 bullet)))
4527 (cond ((listp format)
4528 (list depth
fd5359c6
MR
4529 (if allout-abbreviate-flattened-numbering
4530 (allout-stringify-flat-index format
9179616f 4531 gone-out)
fd5359c6 4532 (allout-stringify-flat-index-plain
9179616f
DL
4533 format))
4534 strings
4535 special))
4536 ((eq format 'indent)
4537 (if special
4538 (list depth
4539 (concat (make-string (1+ depth) ? )
4540 (substring prefix -1))
4541 strings)
4542 (list depth
4543 (make-string depth ? )
4544 strings)))
fd5359c6 4545 (t (error "allout-listify-exposed: %s %s"
9179616f
DL
4546 "invalid format" format))))
4547 (list depth prefix strings))
4548 result))
4549 ;; Reasses format, if any:
4550 (if (and format (listp format))
4551 (cond ((= new-depth depth)
4552 (setq format (cons (1+ (car format))
4553 (cdr format))))
4554 ((> new-depth depth) ; descending - assume by 1:
4555 (setq format (cons 1 format)))
4556 (t
4557 ; Pop the residue:
4558 (while (< new-depth depth)
4559 (setq format (cdr format))
4560 (setq depth (1- depth)))
4561 ; And increment the current one:
4562 (setq format
4563 (cons (1+ (or (car format)
4564 -1))
4565 (cdr format)))))))
4566 ;; Put the list with first at front, to last at back:
4567 (nreverse result))))
d82979ea
EZ
4568;;;_ > my-region-active-p ()
4569(defmacro my-region-active-p ()
4570 (if (fboundp 'region-active-p)
4571 '(region-active-p)
4572 'mark-active))
fd5359c6 4573;;;_ > allout-process-exposed (&optional func from to frombuf
9179616f 4574;;; tobuf format)
fd5359c6 4575(defun allout-process-exposed (&optional func from to frombuf tobuf
96b83743 4576 format start-num)
9179616f 4577 "Map function on exposed parts of current topic; results to another buffer.
353e2ef2 4578
d82979ea
EZ
4579All args are options; default values itemized below.
4580
4581Apply FUNCTION to exposed portions FROM position TO position in buffer
9179616f
DL
4582FROMBUF to buffer TOBUF. Sixth optional arg, FORMAT, designates an
4583alternate presentation form:
c567ac01 4584
9179616f
DL
4585 `flat' - Present prefix as numeric section.subsection..., starting with
4586 section indicated by the start-num, innermost nesting first.
4587 X`flat-indented' - Prefix is like `flat' for first topic at each
4588 X level, but subsequent topics have only leaf topic
4589 X number, padded with blanks to line up with first.
4590 `indent' \(symbol) - Convert header prefixes to all white space,
4591 except for distinctive bullets.
c567ac01 4592
9179616f 4593Defaults:
d82979ea 4594 FUNCTION: `allout-insert-listified'
9179616f
DL
4595 FROM: region start, if region active, else start of buffer
4596 TO: region end, if region active, else end of buffer
4597 FROMBUF: current buffer
4598 TOBUF: buffer name derived: \"*current-buffer-name exposed*\"
4599 FORMAT: nil"
4600
4601 ; Resolve arguments,
4602 ; defaulting if necessary:
fd5359c6 4603 (if (not func) (setq func 'allout-insert-listified))
9179616f
DL
4604 (if (not (and from to))
4605 (if (my-region-active-p)
4606 (setq from (region-beginning) to (region-end))
4607 (setq from (point-min) to (point-max))))
4608 (if frombuf
4609 (if (not (bufferp frombuf))
4610 ;; Specified but not a buffer - get it:
4611 (let ((got (get-buffer frombuf)))
4612 (if (not got)
fd5359c6 4613 (error (concat "allout-process-exposed: source buffer "
9179616f
DL
4614 frombuf
4615 " not found."))
4616 (setq frombuf got))))
4617 ;; not specified - default it:
4618 (setq frombuf (current-buffer)))
4619 (if tobuf
4620 (if (not (bufferp tobuf))
4621 (setq tobuf (get-buffer-create tobuf)))
4622 ;; not specified - default it:
4623 (setq tobuf (concat "*" (buffer-name frombuf) " exposed*")))
4624 (if (listp format)
4625 (nreverse format))
4626
4627 (let* ((listified
4628 (progn (set-buffer frombuf)
fd5359c6 4629 (allout-listify-exposed from to format))))
9179616f
DL
4630 (set-buffer tobuf)
4631 (mapcar func listified)
4632 (pop-to-buffer tobuf)))
4633
4634;;;_ - Copy exposed
fd5359c6
MR
4635;;;_ > allout-insert-listified (listified)
4636(defun allout-insert-listified (listified)
9179616f
DL
4637 "Insert contents of listified outline portion in current buffer.
4638
e126900f 4639LISTIFIED is a list representing each topic header and body:
9179616f
DL
4640
4641 \`(depth prefix text)'
4642
d82979ea 4643or \`(depth prefix text bullet-plus)'
9179616f
DL
4644
4645If `bullet-plus' is specified, it is inserted just after the entire prefix."
4646 (setq listified (cdr listified))
4647 (let ((prefix (prog1
4648 (car listified)
4649 (setq listified (cdr listified))))
4650 (text (prog1
4651 (car listified)
4652 (setq listified (cdr listified))))
4653 (bullet-plus (car listified)))
eac9cf5f
PJ
4654 (insert prefix)
4655 (if bullet-plus (insert (concat " " bullet-plus)))
9179616f 4656 (while text
eac9cf5f 4657 (insert (car text))
9179616f 4658 (if (setq text (cdr text))
0949617b 4659 (insert "\n")))
eac9cf5f 4660 (insert "\n")))
fd5359c6
MR
4661;;;_ > allout-copy-exposed-to-buffer (&optional arg tobuf format)
4662(defun allout-copy-exposed-to-buffer (&optional arg tobuf format)
9179616f
DL
4663 "Duplicate exposed portions of current outline to another buffer.
4664
4665Other buffer has current buffers name with \" exposed\" appended to it.
4666
4667With repeat count, copy the exposed parts of only the current topic.
4668
4669Optional second arg TOBUF is target buffer name.
4670
4671Optional third arg FORMAT, if non-nil, symbolically designates an
4672alternate presentation format for the outline:
4673
4674 `flat' - Convert topic header prefixes to numeric
4675 section.subsection... identifiers.
4676 `indent' - Convert header prefixes to all white space, except for
4677 distinctive bullets.
4678 `indent-flat' - The best of both - only the first of each level has
4679 the full path, the rest have only the section number
4680 of the leaf, preceded by the right amount of indentation."
c567ac01
RS
4681
4682 (interactive "P")
4683 (if (not tobuf)
4684 (setq tobuf (get-buffer-create (concat "*" (buffer-name) " exposed*"))))
4685 (let* ((start-pt (point))
fd5359c6
MR
4686 (beg (if arg (allout-back-to-current-heading) (point-min)))
4687 (end (if arg (allout-end-of-current-subtree) (point-max)))
9179616f
DL
4688 (buf (current-buffer))
4689 (start-list ()))
4690 (if (eq format 'flat)
4691 (setq format (if arg (save-excursion
4692 (goto-char beg)
fd5359c6 4693 (allout-topic-flat-index))
9179616f 4694 '(1))))
c567ac01 4695 (save-excursion (set-buffer tobuf)(erase-buffer))
fd5359c6 4696 (allout-process-exposed 'allout-insert-listified
c567ac01
RS
4697 beg
4698 end
4699 (current-buffer)
9179616f
DL
4700 tobuf
4701 format start-list)
c567ac01
RS
4702 (goto-char (point-min))
4703 (pop-to-buffer buf)
4704 (goto-char start-pt)))
fd5359c6
MR
4705;;;_ > allout-flatten-exposed-to-buffer (&optional arg tobuf)
4706(defun allout-flatten-exposed-to-buffer (&optional arg tobuf)
9179616f
DL
4707 "Present numeric outline of outline's exposed portions in another buffer.
4708
e126900f 4709The resulting outline is not compatible with outline mode - use
fd5359c6 4710`allout-copy-exposed-to-buffer' if you want that.
9179616f 4711
fd5359c6 4712Use `allout-indented-exposed-to-buffer' for indented presentation.
9179616f
DL
4713
4714With repeat count, copy the exposed portions of only current topic.
4715
e126900f 4716Other buffer has current buffer's name with \" exposed\" appended to
9179616f
DL
4717it, unless optional second arg TOBUF is specified, in which case it is
4718used verbatim."
4719 (interactive "P")
fd5359c6
MR
4720 (allout-copy-exposed-to-buffer arg tobuf 'flat))
4721;;;_ > allout-indented-exposed-to-buffer (&optional arg tobuf)
4722(defun allout-indented-exposed-to-buffer (&optional arg tobuf)
9179616f
DL
4723 "Present indented outline of outline's exposed portions in another buffer.
4724
e126900f 4725The resulting outline is not compatible with outline mode - use
fd5359c6 4726`allout-copy-exposed-to-buffer' if you want that.
9179616f 4727
fd5359c6 4728Use `allout-flatten-exposed-to-buffer' for numeric sectional presentation.
9179616f
DL
4729
4730With repeat count, copy the exposed portions of only current topic.
4731
e126900f 4732Other buffer has current buffer's name with \" exposed\" appended to
9179616f
DL
4733it, unless optional second arg TOBUF is specified, in which case it is
4734used verbatim."
4735 (interactive "P")
fd5359c6 4736 (allout-copy-exposed-to-buffer arg tobuf 'indent))
c567ac01
RS
4737
4738;;;_ - LaTeX formatting
e126900f
JB
4739;;;_ > allout-latex-verb-quote (string &optional flow)
4740(defun allout-latex-verb-quote (string &optional flow)
539d7736 4741 "Return copy of STRING for literal reproduction across LaTeX processing.
19b84ba3 4742Expresses the original characters \(including carriage returns) of the
539d7736 4743string across LaTeX processing."
9179616f
DL
4744 (mapconcat (function
4745 (lambda (char)
c567ac01
RS
4746 (cond ((memq char '(?\\ ?$ ?% ?# ?& ?{ ?} ?_ ?^ ?- ?*))
4747 (concat "\\char" (number-to-string char) "{}"))
4748 ((= char ?\n) "\\\\")
9179616f 4749 (t (char-to-string char)))))
e126900f 4750 string
c567ac01 4751 ""))
fd5359c6
MR
4752;;;_ > allout-latex-verbatim-quote-curr-line ()
4753(defun allout-latex-verbatim-quote-curr-line ()
539d7736 4754 "Express line for exact \(literal) representation across LaTeX processing.
19b84ba3
RS
4755
4756Adjust line contents so it is unaltered \(from the original line)
539d7736 4757across LaTeX processing, within the context of a `verbatim'
c567ac01 4758environment. Leaves point at the end of the line."
0458fa80
EZ
4759 (let ((inhibit-field-text-motion t))
4760 (beginning-of-line)
4761 (let ((beg (point))
4762 (end (progn (end-of-line)(point))))
4763 (goto-char beg)
4764 (while (re-search-forward "\\\\"
4765 ;;"\\\\\\|\\{\\|\\}\\|\\_\\|\\$\\|\\\"\\|\\&\\|\\^\\|\\-\\|\\*\\|#"
4766 end ; bounded by end-of-line
4767 1) ; no matches, move to end & return nil
4768 (goto-char (match-beginning 0))
4769 (insert "\\")
4770 (setq end (1+ end))
4771 (goto-char (1+ (match-end 0)))))))
e126900f
JB
4772;;;_ > allout-insert-latex-header (buffer)
4773(defun allout-insert-latex-header (buffer)
539d7736 4774 "Insert initial LaTeX commands at point in BUFFER."
c567ac01
RS
4775 ;; Much of this is being derived from the stuff in appendix of E in
4776 ;; the TeXBook, pg 421.
e126900f 4777 (set-buffer buffer)
c567ac01
RS
4778 (let ((doc-style (format "\n\\documentstyle{%s}\n"
4779 "report"))
fd5359c6 4780 (page-numbering (if allout-number-pages
c567ac01
RS
4781 "\\pagestyle{empty}\n"
4782 ""))
c567ac01 4783 (titlecmd (format "\\newcommand{\\titlecmd}[1]{{%s #1}}\n"
fd5359c6 4784 allout-title-style))
c567ac01 4785 (labelcmd (format "\\newcommand{\\labelcmd}[1]{{%s #1}}\n"
fd5359c6 4786 allout-label-style))
c567ac01 4787 (headlinecmd (format "\\newcommand{\\headlinecmd}[1]{{%s #1}}\n"
fd5359c6 4788 allout-head-line-style))
c567ac01 4789 (bodylinecmd (format "\\newcommand{\\bodylinecmd}[1]{{%s #1}}\n"
fd5359c6 4790 allout-body-line-style))
c567ac01
RS
4791 (setlength (format "%s%s%s%s"
4792 "\\newlength{\\stepsize}\n"
4793 "\\setlength{\\stepsize}{"
fd5359c6 4794 allout-indent
c567ac01
RS
4795 "}\n"))
4796 (oneheadline (format "%s%s%s%s%s%s%s"
4797 "\\newcommand{\\OneHeadLine}[3]{%\n"
4798 "\\noindent%\n"
4799 "\\hspace*{#2\\stepsize}%\n"
4800 "\\labelcmd{#1}\\hspace*{.2cm}"
4801 "\\headlinecmd{#3}\\\\["
fd5359c6 4802 allout-line-skip
c567ac01
RS
4803 "]\n}\n"))
4804 (onebodyline (format "%s%s%s%s%s%s"
4805 "\\newcommand{\\OneBodyLine}[2]{%\n"
4806 "\\noindent%\n"
4807 "\\hspace*{#1\\stepsize}%\n"
4808 "\\bodylinecmd{#2}\\\\["
fd5359c6 4809 allout-line-skip
c567ac01
RS
4810 "]\n}\n"))
4811 (begindoc "\\begin{document}\n\\begin{center}\n")
4812 (title (format "%s%s%s%s"
4813 "\\titlecmd{"
fd5359c6 4814 (allout-latex-verb-quote (if allout-title
96b83743 4815 (condition-case nil
fd5359c6 4816 (eval allout-title)
9179616f 4817 ('error "<unnamed buffer>"))
c567ac01
RS
4818 "Unnamed Outline"))
4819 "}\n"
4820 "\\end{center}\n\n"))
4821 (hsize "\\hsize = 7.5 true in\n")
4822 (hoffset "\\hoffset = -1.5 true in\n")
4823 (vspace "\\vspace{.1cm}\n\n"))
4824 (insert (concat doc-style
4825 page-numbering
4826 titlecmd
4827 labelcmd
4828 headlinecmd
4829 bodylinecmd
4830 setlength
4831 oneheadline
4832 onebodyline
4833 begindoc
4834 title
4835 hsize
4836 hoffset
4837 vspace)
4838 )))
e126900f
JB
4839;;;_ > allout-insert-latex-trailer (buffer)
4840(defun allout-insert-latex-trailer (buffer)
539d7736 4841 "Insert concluding LaTeX commands at point in BUFFER."
e126900f 4842 (set-buffer buffer)
c567ac01 4843 (insert "\n\\end{document}\n"))
fd5359c6
MR
4844;;;_ > allout-latexify-one-item (depth prefix bullet text)
4845(defun allout-latexify-one-item (depth prefix bullet text)
19b84ba3
RS
4846 "Insert LaTeX commands for formatting one outline item.
4847
9179616f 4848Args are the topics numeric DEPTH, the header PREFIX lead string, the
19b84ba3 4849BULLET string, and a list of TEXT strings for the body."
c567ac01
RS
4850 (let* ((head-line (if text (car text)))
4851 (body-lines (cdr text))
4852 (curr-line)
4853 body-content bop)
4854 ; Do the head line:
71296446 4855 (insert (concat "\\OneHeadLine{\\verb\1 "
d82979ea
EZ
4856 (allout-latex-verb-quote bullet)
4857 "\1}{"
4858 depth
4859 "}{\\verb\1 "
4860 (if head-line
4861 (allout-latex-verb-quote head-line)
4862 "")
4863 "\1}\n"))
c567ac01
RS
4864 (if (not body-lines)
4865 nil
eac9cf5f
PJ
4866 ;;(insert "\\beginlines\n")
4867 (insert "\\begin{verbatim}\n")
c567ac01
RS
4868 (while body-lines
4869 (setq curr-line (car body-lines))
4870 (if (and (not body-content)
4871 (not (string-match "^\\s-*$" curr-line)))
4872 (setq body-content t))
4873 ; Mangle any occurrences of
4874 ; "\end{verbatim}" in text,
4875 ; it's special:
4876 (if (and body-content
4877 (setq bop (string-match "\\end{verbatim}" curr-line)))
4878 (setq curr-line (concat (substring curr-line 0 bop)
4879 ">"
4880 (substring curr-line bop))))
eac9cf5f
PJ
4881 ;;(insert "|" (car body-lines) "|")
4882 (insert curr-line)
fd5359c6 4883 (allout-latex-verbatim-quote-curr-line)
eac9cf5f 4884 (insert "\n")
c567ac01
RS
4885 (setq body-lines (cdr body-lines)))
4886 (if body-content
4887 (setq body-content nil)
4888 (forward-char -1)
eac9cf5f 4889 (insert "\\ ")
c567ac01 4890 (forward-char 1))
eac9cf5f
PJ
4891 ;;(insert "\\endlines\n")
4892 (insert "\\end{verbatim}\n")
c567ac01 4893 )))
fd5359c6
MR
4894;;;_ > allout-latexify-exposed (arg &optional tobuf)
4895(defun allout-latexify-exposed (arg &optional tobuf)
539d7736 4896 "Format current topics exposed portions to TOBUF for LaTeX processing.
19b84ba3
RS
4897TOBUF defaults to a buffer named the same as the current buffer, but
4898with \"*\" prepended and \" latex-formed*\" appended.
c567ac01
RS
4899
4900With repeat count, copy the exposed portions of entire buffer."
4901
4902 (interactive "P")
4903 (if (not tobuf)
4904 (setq tobuf
4905 (get-buffer-create (concat "*" (buffer-name) " latexified*"))))
4906 (let* ((start-pt (point))
fd5359c6
MR
4907 (beg (if arg (point-min) (allout-back-to-current-heading)))
4908 (end (if arg (point-max) (allout-end-of-current-subtree)))
c567ac01
RS
4909 (buf (current-buffer)))
4910 (set-buffer tobuf)
1977b8f6 4911 (erase-buffer)
fd5359c6 4912 (allout-insert-latex-header tobuf)
c567ac01 4913 (goto-char (point-max))
fd5359c6 4914 (allout-process-exposed 'allout-latexify-one-item
c567ac01
RS
4915 beg
4916 end
4917 buf
4918 tobuf)
4919 (goto-char (point-max))
fd5359c6 4920 (allout-insert-latex-trailer tobuf)
1977b8f6 4921 (goto-char (point-min))
c567ac01
RS
4922 (pop-to-buffer buf)
4923 (goto-char start-pt)))
4924
d82979ea 4925;;;_ #8 Encryption
0949617b
EZ
4926;;;_ > allout-toggle-current-subtree-encryption (&optional fetch-pass)
4927(defun allout-toggle-current-subtree-encryption (&optional fetch-pass)
69027dea 4928 "Encrypt clear or decrypt encoded text of visibly-containing topic's contents.
d82979ea 4929
0949617b
EZ
4930Optional FETCH-PASS universal argument provokes key-pair encryption with
4931single universal argument. With doubled universal argument \(value = 16),
4932it forces prompting for the passphrase regardless of availability from the
4933passphrase cache. With no universal argument, the appropriate passphrase
69027dea 4934is obtained from the cache, if available, else from the user.
d82979ea
EZ
4935
4936Currently only GnuPG encryption is supported.
4937
4938\**NOTE WELL** that the encrypted text must be ascii-armored. For gnupg
4939encryption, include the option ``armor'' in your ~/.gnupg/gpg.conf file.
4940
4941Both symmetric-key and key-pair encryption is implemented. Symmetric is
4942the default, use a single \(x4) universal argument for keypair mode.
4943
4944Encrypted topic's bullet is set to a `~' to signal that the contents of the
4945topic \(body and subtopics, but not heading) is pending encryption or
0949617b
EZ
4946encrypted. `*' asterisk immediately after the bullet signals that the body
4947is encrypted, its' absence means the topic is meant to be encrypted but is
4948not. When a file with topics pending encryption is saved, topics pending
4949encryption are encrypted. See allout-encrypt-unencrypted-on-saves for
4950auto-encryption specifics.
4951
4952\**NOTE WELL** that automatic encryption that happens during saves will
4953default to symmetric encryption - you must manually \(re)encrypt key-pair
4954encrypted topics if you want them to continue to use the key-pair cipher.
d82979ea
EZ
4955
4956Level-1 topics, with prefix consisting solely of an `*' asterisk, cannot be
4957encrypted. If you want to encrypt the contents of a top-level topic, use
4958\\[allout-shift-in] to increase its depth.
4959
0949617b
EZ
4960 Passphrase Caching
4961
4962The encryption passphrase is solicited if not currently available in the
4963passphrase cache from a recent encryption action.
4964
4965The solicited passphrase is retained for reuse in a buffer-specific cache
4966for some set period of time \(default, 60 seconds), after which the string
4967is nulled. The passphrase cache timeout is customized by setting
4968`pgg-passphrase-cache-expiry'.
4969
4970 Symmetric Passphrase Hinting and Verification
4971
4972If the file previously had no associated passphrase, or had a different
4973passphrase than specified, the user is prompted to repeat the new one for
4974corroboration. A random string encrypted by the new passphrase is set on
4975the buffer-specific variable `allout-passphrase-verifier-string', for
4976confirmation of the passphrase when next obtained, before encrypting or
4977decrypting anything with it. This helps avoid mistakenly shifting between
4978keys.
4979
4980If allout customization var `allout-passphrase-verifier-handling' is
4981non-nil, an entry for `allout-passphrase-verifier-string' and its value is
4982added to an Emacs 'local variables' section at the end of the file, which
4983is created if necessary. That setting is for retention of the passphrase
4984verifier across emacs sessions.
4985
4986Similarly, `allout-passphrase-hint-string' stores a user-provided reminder
4987about their passphrase, and `allout-passphrase-hint-handling' specifies
4988when the hint is presented, or if passphrase hints are disabled. If
4989enabled \(see the `allout-passphrase-hint-handling' docstring for details),
4990the hint string is stored in the local-variables section of the file, and
4991solicited whenever the passphrase is changed."
69027dea
EZ
4992 (interactive "P")
4993 (save-excursion
4994 (allout-back-to-current-heading)
96b83743 4995 (allout-toggle-subtree-encryption fetch-pass)
69027dea
EZ
4996 )
4997 )
4998;;;_ > allout-toggle-subtree-encryption (&optional fetch-pass)
4999(defun allout-toggle-subtree-encryption (&optional fetch-pass)
5000 "Encrypt clear text or decrypt encoded topic contents \(body and subtopics.)
5001
5002Optional FETCH-PASS universal argument provokes key-pair encryption with
5003single universal argument. With doubled universal argument \(value = 16),
5004it forces prompting for the passphrase regardless of availability from the
5005passphrase cache. With no universal argument, the appropriate passphrase
5006is obtained from the cache, if available, else from the user.
5007
5008Currently only GnuPG encryption is supported.
5009
5010\**NOTE WELL** that the encrypted text must be ascii-armored. For gnupg
5011encryption, include the option ``armor'' in your ~/.gnupg/gpg.conf file.
5012
5013See `allout-toggle-current-subtree-encryption' for more details."
d82979ea
EZ
5014
5015 (interactive "P")
5016 (save-excursion
5017 (allout-end-of-prefix t)
5018
5019 (if (= (allout-recent-depth) 1)
5020 (error (concat "Cannot encrypt or decrypt level 1 topics -"
5021 " shift it in to make it encryptable")))
5022
d82979ea
EZ
5023 (let* ((allout-buffer (current-buffer))
5024 ;; Asses location:
5025 (after-bullet-pos (point))
5026 (was-encrypted
5027 (progn (if (= (point-max) after-bullet-pos)
5028 (error "no body to encrypt"))
0949617b 5029 (allout-encrypted-topic-p)))
96b83743 5030 (was-collapsed (if (not (search-forward "\n" nil t))
d82979ea
EZ
5031 nil
5032 (backward-char 1)
96b83743 5033 (allout-hidden-p)))
d82979ea
EZ
5034 (subtree-beg (1+ (point)))
5035 (subtree-end (allout-end-of-subtree))
5036 (subject-text (buffer-substring-no-properties subtree-beg
5037 subtree-end))
5038 (subtree-end-char (char-after (1- subtree-end)))
96b83743
EZ
5039 (subtree-trailing-char (char-after subtree-end))
5040 ;; kluge - result-text needs to be nil, but we also want to
5041 ;; check for the error condition
5042 (result-text (if (or (string= "" subject-text)
5043 (string= "\n" subject-text))
5044 (error "No topic contents to %scrypt"
5045 (if was-encrypted "de" "en"))
5046 nil))
d82979ea 5047 ;; Assess key parameters:
0949617b 5048 (key-info (or
d82979ea
EZ
5049 ;; detect the type by which it is already encrypted
5050 (and was-encrypted
0949617b
EZ
5051 (allout-encrypted-key-info subject-text))
5052 (and (member fetch-pass '(4 (4)))
5053 '(keypair nil))
5054 '(symmetric nil)))
5055 (for-key-type (car key-info))
5056 (for-key-identity (cadr key-info))
96b83743 5057 (fetch-pass (and fetch-pass (member fetch-pass '(16 (16))))))
d82979ea
EZ
5058
5059 (setq result-text
5060 (allout-encrypt-string subject-text was-encrypted
0949617b
EZ
5061 (current-buffer)
5062 for-key-type for-key-identity fetch-pass))
d82979ea
EZ
5063
5064 ;; Replace the subtree with the processed product.
5065 (allout-unprotected
5066 (progn
5067 (set-buffer allout-buffer)
5068 (delete-region subtree-beg subtree-end)
5069 (insert result-text)
5070 (if was-collapsed
96b83743
EZ
5071 (allout-flag-region (1- subtree-beg) (point) t))
5072 ;; adjust trailing-blank-lines to preserve topic spacing:
d82979ea 5073 (if (not was-encrypted)
96b83743
EZ
5074 (if (and (= subtree-end-char ?\n)
5075 (= subtree-trailing-char ?\n))
5076 (insert subtree-trailing-char)))
d82979ea
EZ
5077 ;; Ensure that the item has an encrypted-entry bullet:
5078 (if (not (string= (buffer-substring-no-properties
5079 (1- after-bullet-pos) after-bullet-pos)
5080 allout-topic-encryption-bullet))
5081 (progn (goto-char (1- after-bullet-pos))
5082 (delete-char 1)
5083 (insert allout-topic-encryption-bullet)))
5084 (if was-encrypted
5085 ;; Remove the is-encrypted bullet qualifier:
5086 (progn (goto-char after-bullet-pos)
5087 (delete-char 1))
5088 ;; Add the is-encrypted bullet qualifier:
5089 (goto-char after-bullet-pos)
5090 (insert "*"))
5091 )
5092 )
5093 )
5094 )
5095 )
0949617b
EZ
5096;;;_ > allout-encrypt-string (text decrypt allout-buffer key-type for-key
5097;;; fetch-pass &optional retried verifying
5098;;; passphrase)
5099(defun allout-encrypt-string (text decrypt allout-buffer key-type for-key
5100 fetch-pass &optional retried verifying
5101 passphrase)
5102 "Encrypt or decrypt message TEXT.
d82979ea 5103
0949617b 5104If DECRYPT is true (default false), then decrypt instead of encrypt.
d82979ea 5105
0949617b 5106FETCH-PASS (default false) forces fresh prompting for the passphrase.
d82979ea 5107
0949617b 5108KEY-TYPE indicates whether to use a 'symmetric or 'keypair cipher.
d82979ea 5109
0949617b
EZ
5110FOR-KEY is human readable identification of the first of the user's
5111eligible secret keys a keypair decryption targets, or else nil.
d82979ea 5112
0949617b
EZ
5113Optional RETRIED is for internal use - conveys the number of failed keys
5114that have been solicited in sequence leading to this current call.
5115
5116Optional PASSPHRASE enables explicit delivery of the decryption passphrase,
5117for verification purposes.
d82979ea 5118
0949617b 5119Returns the resulting string, or nil if the transformation fails."
d82979ea 5120
0949617b
EZ
5121 (require 'pgg)
5122
69027dea
EZ
5123 (if (not (fboundp 'pgg-encrypt-symmetric))
5124 (error "Allout encryption depends on a newer version of pgg"))
5125
0949617b
EZ
5126 (let* ((scheme (upcase
5127 (format "%s" (or pgg-scheme pgg-default-scheme "GPG"))))
5128 (for-key (and (equal key-type 'keypair)
5129 (or for-key
5130 (split-string (read-string
5131 (format "%s message recipients: "
5132 scheme))
5133 "[ \t,]+"))))
5134 (target-prompt-id (if (equal key-type 'keypair)
5135 (if (= (length for-key) 1)
5136 (car for-key) for-key)
5137 (buffer-name allout-buffer)))
5138 (target-cache-id (format "%s-%s"
5139 key-type
5140 (if (equal key-type 'keypair)
5141 target-prompt-id
5142 (or (buffer-file-name allout-buffer)
5143 target-prompt-id))))
96b83743 5144 result-text status)
0949617b
EZ
5145
5146 (if (and fetch-pass (not passphrase))
5147 ;; Force later fetch by evicting passphrase from the cache.
5148 (pgg-remove-passphrase-from-cache target-cache-id t))
5149
5150 (catch 'encryption-failed
5151
5152 ;; Obtain the passphrase if we don't already have one and we're not
5153 ;; doing a keypair encryption:
5154 (if (not (or passphrase
5155 (and (equal key-type 'keypair)
5156 (not decrypt))))
5157
5158 (setq passphrase (allout-obtain-passphrase for-key
5159 target-cache-id
5160 target-prompt-id
5161 key-type
5162 allout-buffer
5163 retried fetch-pass)))
5164 (with-temp-buffer
5165
96b83743 5166 (insert text)
d82979ea 5167
0949617b 5168 (cond
d82979ea 5169
0949617b
EZ
5170 ;; symmetric:
5171 ((equal key-type 'symmetric)
5172 (setq status
5173 (if decrypt
5174
5175 (pgg-decrypt (point-min) (point-max) passphrase)
5176
5177 (pgg-encrypt-symmetric (point-min) (point-max)
5178 passphrase)))
5179
5180 (if status
5181 (pgg-situate-output (point-min) (point-max))
5182 ;; failed - handle passphrase caching
5183 (if verifying
5184 (throw 'encryption-failed nil)
5185 (pgg-remove-passphrase-from-cache target-cache-id t)
5186 (error "Symmetric-cipher encryption failed - %s"
5187 "try again with different passphrase."))))
5188
5189 ;; encrypt 'keypair:
5190 ((not decrypt)
5191
5192 (setq status
5193
5194 (pgg-encrypt for-key
5195 nil (point-min) (point-max) passphrase))
5196
5197 (if status
5198 (pgg-situate-output (point-min) (point-max))
5199 (error (pgg-remove-passphrase-from-cache target-cache-id t)
5200 (error "encryption failed"))))
5201
5202 ;; decrypt 'keypair:
5203 (t
5204
5205 (setq status
5206 (pgg-decrypt (point-min) (point-max) passphrase))
5207
5208 (if status
5209 (pgg-situate-output (point-min) (point-max))
5210 (error (pgg-remove-passphrase-from-cache target-cache-id t)
5211 (error "decryption failed"))))
5212 )
5213
5214 (setq result-text
5215 (buffer-substring 1 (- (point-max) (if decrypt 0 1))))
5216
5217 ;; validate result - non-empty
5218 (cond ((not result-text)
5219 (if verifying
5220 nil
5221 ;; transform was fruitless, retry w/new passphrase.
5222 (pgg-remove-passphrase-from-cache target-cache-id t)
5223 (allout-encrypt-string text allout-buffer decrypt nil
5224 (if retried (1+ retried) 1)
5225 passphrase)))
5226
5227 ;; Barf if encryption yields extraordinary control chars:
5228 ((and (not decrypt)
5229 (string-match "[\C-a\C-k\C-o-\C-z\C-@]"
5230 result-text))
5231 (error (concat "encryption produced unusable"
5232 " non-armored text - reconfigure!")))
5233
5234 ;; valid result and just verifying or non-symmetric:
5235 ((or verifying (not (equal key-type 'symmetric)))
5236 (if (or verifying decrypt)
5237 (pgg-add-passphrase-to-cache target-cache-id
5238 passphrase t))
5239 result-text)
5240
5241 ;; valid result and regular symmetric - "register"
5242 ;; passphrase with mnemonic aids/cache.
5243 (t
5244 (set-buffer allout-buffer)
5245 (if passphrase
5246 (pgg-add-passphrase-to-cache target-cache-id
5247 passphrase t))
5248 (allout-update-passphrase-mnemonic-aids for-key passphrase
5249 allout-buffer)
5250 result-text)
5251 )
d82979ea
EZ
5252 )
5253 )
d82979ea
EZ
5254 )
5255 )
0949617b
EZ
5256;;;_ > allout-obtain-passphrase (for-key cache-id prompt-id key-type
5257;;; allout-buffer retried fetch-pass)
4034b0e2 5258(defun allout-obtain-passphrase (for-key cache-id prompt-id key-type
0949617b
EZ
5259 allout-buffer retried fetch-pass)
5260 "Obtain passphrase for a key from the cache or else from the user.
5261
5262When obtaining from the user, symmetric-cipher passphrases are verified
5263against either, if available and enabled, a random string that was
5264encrypted against the passphrase, or else against repeated entry by the
5265user for corroboration.
5266
5267FOR-KEY is the key for which the passphrase is being obtained.
5268
5269CACHE-ID is the cache id of the key for the passphrase.
5270
5271PROMPT-ID is the id for use when prompting the user.
5272
5273KEY-TYPE is either 'symmetric or 'keypair.
5274
5275ALLOUT-BUFFER is the buffer containing the entry being en/decrypted.
5276
5277RETRIED is the number of this attempt to obtain this passphrase.
5278
5279FETCH-PASS causes the passphrase to be solicited from the user, regardless
5280of the availability of a cached copy."
d82979ea
EZ
5281
5282 (if (not (equal key-type 'symmetric))
0949617b
EZ
5283 ;; do regular passphrase read on non-symmetric passphrase:
5284 (pgg-read-passphrase (format "%s passphrase%s: "
5285 (upcase (format "%s" (or pgg-scheme
5286 pgg-default-scheme
5287 "GPG")))
5288 (if prompt-id
5289 (format " for %s" prompt-id)
5290 ""))
5291 cache-id t)
d82979ea
EZ
5292
5293 ;; Symmetric hereon:
5294
5295 (save-excursion
5296 (set-buffer allout-buffer)
0949617b
EZ
5297 (let* ((hint (if (and (not (string= allout-passphrase-hint-string ""))
5298 (or (equal allout-passphrase-hint-handling 'always)
5299 (and (equal allout-passphrase-hint-handling
5300 'needed)
d82979ea 5301 retried)))
0949617b 5302 (format " [%s]" allout-passphrase-hint-string)
d82979ea
EZ
5303 ""))
5304 (retry-message (if retried (format " (%s retry)" retried) ""))
0949617b
EZ
5305 (prompt-sans-hint (format "'%s' symmetric passphrase%s: "
5306 prompt-id retry-message))
5307 (full-prompt (format "'%s' symmetric passphrase%s%s: "
5308 prompt-id hint retry-message))
d82979ea 5309 (prompt full-prompt)
0949617b
EZ
5310 (verifier-string (allout-get-encryption-passphrase-verifier))
5311
5312 (cached (and (not fetch-pass)
5313 (pgg-read-passphrase-from-cache cache-id t)))
5314 (got-pass (or cached
5315 (pgg-read-passphrase full-prompt cache-id t)))
5316
d82979ea
EZ
5317 confirmation)
5318
0949617b 5319 (if (not got-pass)
d82979ea
EZ
5320 nil
5321
0949617b 5322 ;; Duplicate our handle on the passphrase so it's not clobbered by
d82979ea 5323 ;; deactivate-passwd memory clearing:
0949617b 5324 (setq got-pass (format "%s" got-pass))
d82979ea
EZ
5325
5326 (cond (verifier-string
0949617b
EZ
5327 (save-window-excursion
5328 (if (allout-encrypt-string verifier-string 'decrypt
5329 allout-buffer 'symmetric
5330 for-key nil 0 'verifying
5331 got-pass)
5332 (setq confirmation (format "%s" got-pass))))
5333
5334 (if (and (not confirmation)
d82979ea 5335 (if (yes-or-no-p
0949617b 5336 (concat "Passphrase differs from established"
d82979ea
EZ
5337 " - use new one instead? "))
5338 ;; deactivate password for subsequent
5339 ;; confirmation:
0949617b
EZ
5340 (progn
5341 (pgg-remove-passphrase-from-cache cache-id t)
5342 (setq prompt prompt-sans-hint)
5343 nil)
d82979ea 5344 t))
0949617b
EZ
5345 (progn (pgg-remove-passphrase-from-cache cache-id t)
5346 (error "Wrong passphrase."))))
5347 ;; No verifier string - force confirmation by repetition of
5348 ;; (new) passphrase:
5349 ((or fetch-pass (not cached))
5350 (pgg-remove-passphrase-from-cache cache-id t))))
5351 ;; confirmation vs new input - doing pgg-read-passphrase will do the
d82979ea 5352 ;; right thing, in either case:
0949617b
EZ
5353 (if (not confirmation)
5354 (setq confirmation
5355 (pgg-read-passphrase (concat prompt
5356 " ... confirm spelling: ")
5357 cache-id t)))
d82979ea 5358 (prog1
0949617b 5359 (if (equal got-pass confirmation)
d82979ea
EZ
5360 confirmation
5361 (if (yes-or-no-p (concat "spelling of original and"
5362 " confirmation differ - retry? "))
5363 (progn (setq retried (if retried (1+ retried) 1))
0949617b 5364 (pgg-remove-passphrase-from-cache cache-id t)
d82979ea 5365 ;; recurse to this routine:
0949617b
EZ
5366 (pgg-read-passphrase prompt-sans-hint cache-id t))
5367 (pgg-remove-passphrase-from-cache cache-id t)
d82979ea
EZ
5368 (error "Confirmation failed.")))
5369 ;; reduce opportunity for memory cherry-picking by zeroing duplicate:
0949617b
EZ
5370 (dotimes (i (length got-pass))
5371 (aset got-pass i 0))
d82979ea
EZ
5372 )
5373 )
5374 )
5375 )
5376 )
d82979ea
EZ
5377;;;_ > allout-encrypted-topic-p ()
5378(defun allout-encrypted-topic-p ()
5379 "True if the current topic is encryptable and encrypted."
5380 (save-excursion
5381 (allout-end-of-prefix t)
5382 (and (string= (buffer-substring-no-properties (1- (point)) (point))
5383 allout-topic-encryption-bullet)
5384 (looking-at "\\*"))
5385 )
5386 )
0949617b
EZ
5387;;;_ > allout-encrypted-key-info (text)
5388;; XXX gpg-specific, alas
5389(defun allout-encrypted-key-info (text)
5390 "Return a pair of the key type and identity of a recipient's secret key.
5391
5392The key type is one of 'symmetric or 'keypair.
d82979ea 5393
0949617b
EZ
5394if 'keypair, and some of the user's secret keys are among those for which
5395the message was encoded, return the identity of the first. otherwise,
5396return nil for the second item of the pair.
d82979ea 5397
0949617b
EZ
5398An error is raised if the text is not encrypted."
5399 (require 'pgg-parse)
d82979ea 5400 (save-excursion
0949617b 5401 (with-temp-buffer
96b83743 5402 (insert text)
0949617b
EZ
5403 (let* ((parsed-armor (pgg-parse-armor-region (point-min) (point-max)))
5404 (type (if (pgg-gpg-symmetric-key-p parsed-armor)
5405 'symmetric
5406 'keypair))
5407 secret-keys first-secret-key for-key-owner)
5408 (if (equal type 'keypair)
5409 (setq secret-keys (pgg-gpg-lookup-all-secret-keys)
5410 first-secret-key (pgg-gpg-select-matching-key parsed-armor
5411 secret-keys)
5412 for-key-owner (and first-secret-key
5413 (pgg-gpg-lookup-key-owner
5414 first-secret-key))))
5415 (list type (pgg-gpg-key-id-from-key-owner for-key-owner))
5416 )
d82979ea
EZ
5417 )
5418 )
5419 )
0949617b
EZ
5420;;;_ > allout-create-encryption-passphrase-verifier (passphrase)
5421(defun allout-create-encryption-passphrase-verifier (passphrase)
5422 "Encrypt random message for later validation of symmetric key's passphrase."
d82979ea
EZ
5423 ;; use 20 random ascii characters, across the entire ascii range.
5424 (random t)
5425 (let ((spew (make-string 20 ?\0)))
5426 (dotimes (i (length spew))
5427 (aset spew i (1+ (random 254))))
0949617b
EZ
5428 (allout-encrypt-string spew nil (current-buffer) 'symmetric
5429 nil nil 0 passphrase))
d82979ea 5430 )
0949617b 5431;;;_ > allout-update-passphrase-mnemonic-aids (for-key passphrase
4034b0e2 5432;;; outline-buffer)
0949617b
EZ
5433(defun allout-update-passphrase-mnemonic-aids (for-key passphrase
5434 outline-buffer)
5435 "Update passphrase verifier and hint strings if necessary.
5436
5437See `allout-passphrase-verifier-string' and `allout-passphrase-hint-string'
5438settings.
5439
5440PASSPHRASE is the passphrase being mnemonicized
5441
5442OUTLINE-BUFFER is the buffer of the outline being adjusted.
5443
5444These are used to help the user keep track of the passphrase they use for
5445symmetric encryption in the file.
5446
5447Behavior is governed by `allout-passphrase-verifier-handling',
5448`allout-passphrase-hint-handling', and also, controlling whether the values
5449are preserved on Emacs local file variables,
5450`allout-enable-file-variable-adjustment'."
5451
5452 ;; If passphrase doesn't agree with current verifier:
5453 ;; - adjust the verifier
5454 ;; - if passphrase hint handling is enabled, adjust the passphrase hint
5455 ;; - if file var settings are enabled, adjust the file vars
5456
5457 (let* ((new-verifier-needed (not (allout-verify-passphrase
5458 for-key passphrase outline-buffer)))
5459 (new-verifier-string
5460 (if new-verifier-needed
5461 ;; Collapse to a single line and enclose in string quotes:
5462 (subst-char-in-string
5463 ?\n ?\C-a (allout-create-encryption-passphrase-verifier
5464 passphrase))))
5465 new-hint)
5466 (when new-verifier-string
5467 ;; do the passphrase hint first, since it's interactive
5468 (when (and allout-passphrase-hint-handling
5469 (not (equal allout-passphrase-hint-handling 'disabled)))
5470 (setq new-hint
5471 (read-from-minibuffer "Passphrase hint to jog your memory: "
5472 allout-passphrase-hint-string))
5473 (when (not (string= new-hint allout-passphrase-hint-string))
5474 (setq allout-passphrase-hint-string new-hint)
5475 (allout-adjust-file-variable "allout-passphrase-hint-string"
5476 allout-passphrase-hint-string)))
5477 (when allout-passphrase-verifier-handling
5478 (setq allout-passphrase-verifier-string new-verifier-string)
5479 (allout-adjust-file-variable "allout-passphrase-verifier-string"
5480 allout-passphrase-verifier-string))
5481 )
d82979ea
EZ
5482 )
5483 )
0949617b
EZ
5484;;;_ > allout-get-encryption-passphrase-verifier ()
5485(defun allout-get-encryption-passphrase-verifier ()
5486 "Return text of the encrypt passphrase verifier, unmassaged, or nil if none.
d82979ea 5487
4034b0e2 5488Derived from value of `allout-passphrase-verifier-string'."
d82979ea 5489
0949617b
EZ
5490 (let ((verifier-string (and (boundp 'allout-passphrase-verifier-string)
5491 allout-passphrase-verifier-string)))
d82979ea
EZ
5492 (if verifier-string
5493 ;; Return it uncollapsed
0949617b 5494 (subst-char-in-string ?\C-a ?\n verifier-string))
d82979ea
EZ
5495 )
5496 )
0949617b
EZ
5497;;;_ > allout-verify-passphrase (key passphrase allout-buffer)
5498(defun allout-verify-passphrase (key passphrase allout-buffer)
5499 "True if passphrase successfully decrypts verifier, nil otherwise.
d82979ea 5500
0949617b 5501\"Otherwise\" includes absence of passphrase verifier."
d82979ea
EZ
5502 (save-excursion
5503 (set-buffer allout-buffer)
0949617b
EZ
5504 (and (boundp 'allout-passphrase-verifier-string)
5505 allout-passphrase-verifier-string
5506 (allout-encrypt-string (allout-get-encryption-passphrase-verifier)
d82979ea 5507 'decrypt allout-buffer 'symmetric
0949617b 5508 key nil 0 'verifying passphrase)
d82979ea
EZ
5509 t)))
5510;;;_ > allout-next-topic-pending-encryption (&optional except-mark)
5511(defun allout-next-topic-pending-encryption (&optional except-mark)
5512 "Return the point of the next topic pending encryption, or nil if none.
5513
5514EXCEPT-MARK identifies a point whose containing topics should be excluded
5515from encryption. This supports 'except-current mode of
5516`allout-encrypt-unencrypted-on-saves'.
5517
5518Such a topic has the allout-topic-encryption-bullet without an
5519immediately following '*' that would mark the topic as being encrypted. It
5520must also have content."
5521 (let (done got content-beg)
5522 (while (not done)
5523
5524 (if (not (re-search-forward
96b83743 5525 (format "\\(\\`\\|\n\\)%s *%s[^*]"
d82979ea
EZ
5526 (regexp-quote allout-header-prefix)
5527 (regexp-quote allout-topic-encryption-bullet))
5528 nil t))
5529 (setq got nil
5530 done t)
5531 (goto-char (setq got (match-beginning 0)))
96b83743 5532 (if (looking-at "\n")
d82979ea
EZ
5533 (forward-char 1))
5534 (setq got (point)))
5535
5536 (cond ((not got)
5537 (setq done t))
5538
96b83743 5539 ((not (search-forward "\n"))
d82979ea
EZ
5540 (setq got nil
5541 done t))
5542
5543 ((eobp)
5544 (setq got nil
5545 done t))
5546
5547 (t
5548 (setq content-beg (point))
5549 (backward-char 1)
5550 (allout-end-of-subtree)
5551 (if (or (<= (point) content-beg)
5552 (and except-mark
5553 (<= content-beg except-mark)
5554 (>= (point) except-mark)))
5555 ;; Continue looking
5556 (setq got nil)
5557 ;; Got it!
5558 (setq done t)))
5559 )
5560 )
5561 (if got
5562 (goto-char got))
5563 )
5564 )
5565;;;_ > allout-encrypt-decrypted (&optional except-mark)
5566(defun allout-encrypt-decrypted (&optional except-mark)
5567 "Encrypt topics pending encryption except those containing exemption point.
5568
5569EXCEPT-MARK identifies a point whose containing topics should be excluded
5570from encryption. This supports 'except-current mode of
5571`allout-encrypt-unencrypted-on-saves'.
5572
5573If a topic that is currently being edited was encrypted, we return a list
5574containing the location of the topic and the location of the cursor just
5575before the topic was encrypted. This can be used, eg, to decrypt the topic
5576and exactly resituate the cursor if this is being done as part of a file
5577save. See `allout-encrypt-unencrypted-on-saves' for more info."
5578
5579 (interactive "p")
5580 (save-excursion
96b83743
EZ
5581 (let* ((current-mark (point-marker))
5582 (current-mark-position (marker-position current-mark))
5583 was-modified
5584 bo-subtree
5585 editing-topic editing-point)
d82979ea
EZ
5586 (goto-char (point-min))
5587 (while (allout-next-topic-pending-encryption except-mark)
5588 (setq was-modified (buffer-modified-p))
96b83743
EZ
5589 (when (save-excursion
5590 (and (boundp 'allout-encrypt-unencrypted-on-saves)
5591 allout-encrypt-unencrypted-on-saves
5592 (setq bo-subtree (re-search-forward "$"))
5593 (not (allout-hidden-p))
5594 (>= current-mark (point))
5595 (allout-end-of-current-subtree)
5596 (<= current-mark (point))))
d82979ea
EZ
5597 (setq editing-topic (point)
5598 ;; we had to wait for this 'til now so prior topics are
5599 ;; encrypted, any relevant text shifts are in place:
96b83743
EZ
5600 editing-point (- current-mark-position
5601 (count-trailing-whitespace-region
5602 bo-subtree current-mark-position))))
69027dea 5603 (allout-toggle-subtree-encryption)
d82979ea
EZ
5604 (if (not was-modified)
5605 (set-buffer-modified-p nil))
5606 )
5607 (if (not was-modified)
5608 (set-buffer-modified-p nil))
5609 (if editing-topic (list editing-topic editing-point))
5610 )
5611 )
5612 )
5613
5614;;;_ #9 miscellaneous
fd5359c6
MR
5615;;;_ > allout-mark-topic ()
5616(defun allout-mark-topic ()
c567ac01
RS
5617 "Put the region around topic currently containing point."
5618 (interactive)
0458fa80
EZ
5619 (let ((inhibit-field-text-motion t))
5620 (beginning-of-line))
fd5359c6 5621 (allout-goto-prefix)
c567ac01 5622 (push-mark (point))
fd5359c6 5623 (allout-end-of-current-subtree)
c567ac01
RS
5624 (exchange-point-and-mark))
5625;;;_ > outlineify-sticky ()
19b84ba3 5626;; outlinify-sticky is correct spelling; provide this alias for sticklers:
0949617b 5627;;;###autoload
19b84ba3 5628(defalias 'outlinify-sticky 'outlineify-sticky)
0949617b 5629;;;###autoload
c567ac01 5630(defun outlineify-sticky (&optional arg)
a0776d6b 5631 "Activate outline mode and establish file var so it is started subsequently.
1977b8f6 5632
fd5359c6 5633See doc-string for `allout-layout' and `allout-init' for details on
19b84ba3
RS
5634setup for auto-startup."
5635
5636 (interactive "P")
5637
fd5359c6 5638 (allout-mode t)
19b84ba3
RS
5639
5640 (save-excursion
5641 (goto-char (point-min))
fd5359c6 5642 (if (looking-at allout-regexp)
19b84ba3 5643 t
fd5359c6 5644 (allout-open-topic 2)
eac9cf5f 5645 (insert (concat "Dummy outline topic header - see"
d82979ea
EZ
5646 "`allout-mode' docstring: `^Hm'."))
5647 (allout-adjust-file-variable
4034b0e2 5648 "allout-layout" (or allout-layout '(-1 : 0))))))
d82979ea
EZ
5649;;;_ > allout-file-vars-section-data ()
5650(defun allout-file-vars-section-data ()
5651 "Return data identifying the file-vars section, or nil if none.
5652
5653Returns list `(beginning-point prefix-string suffix-string)'."
5654 ;; minimally gleaned from emacs 21.4 files.el hack-local-variables function.
5655 (let (beg prefix suffix)
5656 (save-excursion
1977b8f6 5657 (goto-char (point-max))
d82979ea
EZ
5658 (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move)
5659 (if (let ((case-fold-search t))
5660 (not (search-forward "Local Variables:" nil t)))
5661 nil
5662 (setq beg (- (point) 16))
5663 (setq suffix (buffer-substring-no-properties
5664 (point)
96b83743 5665 (progn (if (search-forward "\n" nil t)
d82979ea
EZ
5666 (forward-char -1))
5667 (point))))
5668 (setq prefix (buffer-substring-no-properties
96b83743 5669 (progn (if (search-backward "\n" nil t)
d82979ea
EZ
5670 (forward-char 1))
5671 (point))
5672 beg))
5673 (list beg prefix suffix))
5674 )
5675 )
5676 )
5677;;;_ > allout-adjust-file-variable (varname value)
5678(defun allout-adjust-file-variable (varname value)
5679 "Adjust the setting of an emacs file variable named VARNAME to VALUE.
5680
5681This activity is inhibited if either `enable-local-variables'
5682`allout-enable-file-variable-adjustment' are nil.
5683
5684When enabled, an entry for the variable is created if not already present,
5685or changed if established with a different value. The section for the file
5686variables, itself, is created if not already present. When created, the
5687section lines \(including the section line) exist as second-level topics in
5688a top-level topic at the end of the file.
5689
5690enable-local-variables must be true for any of this to happen."
5691 (if (not (and enable-local-variables
5692 allout-enable-file-variable-adjustment))
5693 nil
5694 (save-excursion
0458fa80
EZ
5695 (let ((inhibit-field-text-motion t)
5696 (section-data (allout-file-vars-section-data))
d82979ea
EZ
5697 beg prefix suffix)
5698 (if section-data
5699 (setq beg (car section-data)
5700 prefix (cadr section-data)
5701 suffix (car (cddr section-data)))
5702 ;; create the section
5703 (goto-char (point-max))
5704 (open-line 1)
5705 (allout-open-topic 0)
5706 (end-of-line)
5707 (insert "Local emacs vars.\n")
5708 (allout-open-topic 1)
5709 (setq beg (point)
5710 suffix ""
5711 prefix (buffer-substring-no-properties (progn
5712 (beginning-of-line)
5713 (point))
5714 beg))
5715 (goto-char beg)
5716 (insert "Local variables:\n")
5717 (allout-open-topic 0)
5718 (insert "End:\n")
5719 )
5720 ;; look for existing entry or create one, leaving point for insertion
5721 ;; of new value:
5722 (goto-char beg)
5723 (allout-show-to-offshoot)
5724 (if (search-forward (concat "\n" prefix varname ":") nil t)
5725 (let* ((value-beg (point))
96b83743 5726 (line-end (progn (if (search-forward "\n" nil t)
d82979ea
EZ
5727 (forward-char -1))
5728 (point)))
5729 (value-end (- line-end (length suffix))))
5730 (if (> value-end value-beg)
5731 (delete-region value-beg value-end)))
5732 (end-of-line)
5733 (open-line 1)
5734 (forward-line 1)
5735 (insert (concat prefix varname ":")))
5736 (insert (format " %S%s" value suffix))
5737 )
5738 )
5739 )
5740 )
c567ac01 5741;;;_ > solicit-char-in-string (prompt string &optional do-defaulting)
1977b8f6 5742(defun solicit-char-in-string (prompt string &optional do-defaulting)
c567ac01 5743 "Solicit (with first arg PROMPT) choice of a character from string STRING.
1977b8f6 5744
c567ac01 5745Optional arg DO-DEFAULTING indicates to accept empty input (CR)."
1977b8f6
RS
5746
5747 (let ((new-prompt prompt)
5748 got)
5749
5750 (while (not got)
5751 (message "%s" new-prompt)
5752
5753 ;; We do our own reading here, so we can circumvent, eg, special
9179616f 5754 ;; treatment for `?' character. (Oughta use minibuffer keymap instead.)
1977b8f6 5755 (setq got
c567ac01 5756 (char-to-string (let ((cursor-in-echo-area nil)) (read-char))))
1977b8f6 5757
9179616f
DL
5758 (setq got
5759 (cond ((string-match (regexp-quote got) string) got)
5760 ((and do-defaulting (string= got "\r"))
5761 ;; Return empty string to default:
5762 "")
5763 ((string= got "\C-g") (signal 'quit nil))
5764 (t
5765 (setq new-prompt (concat prompt
5766 got
5767 " ...pick from: "
5768 string
5769 ""))
5770 nil))))
5771 ;; got something out of loop - return it:
5772 got)
1977b8f6 5773 )
c567ac01
RS
5774;;;_ > regexp-sans-escapes (string)
5775(defun regexp-sans-escapes (regexp &optional successive-backslashes)
5776 "Return a copy of REGEXP with all character escapes stripped out.
19b84ba3 5777
c567ac01
RS
5778Representations of actual backslashes - '\\\\\\\\' - are left as a
5779single backslash.
5780
d82979ea 5781Optional arg SUCCESSIVE-BACKSLASHES is used internally for recursion."
c567ac01
RS
5782
5783 (if (string= regexp "")
5784 ""
5785 ;; Set successive-backslashes to number if current char is
5786 ;; backslash, or else to nil:
5787 (setq successive-backslashes
5788 (if (= (aref regexp 0) ?\\)
5789 (if successive-backslashes (1+ successive-backslashes) 1)
5790 nil))
5791 (if (or (not successive-backslashes) (= 2 successive-backslashes))
5792 ;; Include first char:
5793 (concat (substring regexp 0 1)
5794 (regexp-sans-escapes (substring regexp 1)))
5795 ;; Exclude first char, but maintain count:
5796 (regexp-sans-escapes (substring regexp 1) successive-backslashes))))
96b83743
EZ
5797;;;_ > count-trailing-whitespace-region (beg end)
5798(defun count-trailing-whitespace-region (beg end)
5799 "Return number of trailing whitespace chars between BEG and END.
5800
5801If BEG is bigger than END we return 0."
5802 (if (> beg end)
5803 0
5804 (save-excursion
5805 (goto-char beg)
5806 (let ((count 0))
5807 (while (re-search-forward "[ ][ ]*$" end t)
5808 (goto-char (1+ (match-beginning 0)))
5809 (setq count (1+ count)))
5810 count))))
5811;;;_ > allout-mark-marker to accommodate divergent emacsen:
5812(defun allout-mark-marker (&optional force buffer)
5813 "Accommodate the different signature for `mark-marker' across Emacsen.
5814
5815XEmacs takes two optional args, while mainline GNU Emacs does not,
5816so pass them along when appropriate."
5817 (if (featurep 'xemacs)
5818 (apply 'mark-marker force buffer)
5819 (mark-marker)))
d82979ea
EZ
5820;;;_ > subst-char-in-string if necessary
5821(if (not (fboundp 'subst-char-in-string))
5822 (defun subst-char-in-string (fromchar tochar string &optional inplace)
5823 "Replace FROMCHAR with TOCHAR in STRING each time it occurs.
5824Unless optional argument INPLACE is non-nil, return a new string."
5825 (let ((i (length string))
5826 (newstr (if inplace string (copy-sequence string))))
5827 (while (> i 0)
5828 (setq i (1- i))
5829 (if (eq (aref newstr i) fromchar)
5830 (aset newstr i tochar)))
5831 newstr)))
96b83743
EZ
5832;;;_ > wholenump if necessary
5833(if (not (fboundp 'wholenump))
5834 (defalias 'wholenump 'natnump))
5835;;;_ > remove-overlays if necessary
5836(if (not (fboundp 'remove-overlays))
5837 (defun remove-overlays (&optional beg end name val)
5838 "Clear BEG and END of overlays whose property NAME has value VAL.
5839Overlays might be moved and/or split.
5840BEG and END default respectively to the beginning and end of buffer."
5841 (unless beg (setq beg (point-min)))
5842 (unless end (setq end (point-max)))
5843 (if (< end beg)
5844 (setq beg (prog1 end (setq end beg))))
5845 (save-excursion
5846 (dolist (o (overlays-in beg end))
5847 (when (eq (overlay-get o name) val)
5848 ;; Either push this overlay outside beg...end
5849 ;; or split it to exclude beg...end
5850 ;; or delete it entirely (if it is contained in beg...end).
5851 (if (< (overlay-start o) beg)
5852 (if (> (overlay-end o) end)
5853 (progn
5854 (move-overlay (copy-overlay o)
5855 (overlay-start o) beg)
5856 (move-overlay o end (overlay-end o)))
5857 (move-overlay o (overlay-start o) beg))
5858 (if (> (overlay-end o) end)
5859 (move-overlay o end (overlay-end o))
5860 (delete-overlay o)))))))
5861 )
5862;;;_ > copy-overlay if necessary - xemacs ~ 21.4
5863(if (not (fboundp 'copy-overlay))
5864 (defun copy-overlay (o)
5865 "Return a copy of overlay O."
5866 (let ((o1 (make-overlay (overlay-start o) (overlay-end o)
5867 ;; FIXME: there's no easy way to find the
5868 ;; insertion-type of the two markers.
5869 (overlay-buffer o)))
5870 (props (overlay-properties o)))
5871 (while props
5872 (overlay-put o1 (pop props) (pop props)))
5873 o1)))
5874;;;_ > add-to-invisibility-spec if necessary - xemacs ~ 21.4
5875(if (not (fboundp 'add-to-invisibility-spec))
5876 (defun add-to-invisibility-spec (element)
5877 "Add ELEMENT to `buffer-invisibility-spec'.
5878See documentation for `buffer-invisibility-spec' for the kind of elements
5879that can be added."
5880 (if (eq buffer-invisibility-spec t)
5881 (setq buffer-invisibility-spec (list t)))
5882 (setq buffer-invisibility-spec
5883 (cons element buffer-invisibility-spec))))
5884;;;_ > remove-from-invisibility-spec if necessary - xemacs ~ 21.4
5885(if (not (fboundp 'remove-from-invisibility-spec))
5886 (defun remove-from-invisibility-spec (element)
5887 "Remove ELEMENT from `buffer-invisibility-spec'."
5888 (if (consp buffer-invisibility-spec)
5889 (setq buffer-invisibility-spec (delete element
5890 buffer-invisibility-spec)))))
5891;;;_ > move-beginning-of-line if necessary - older emacs, xemacs
5892(if (not (fboundp 'move-beginning-of-line))
5893 (defun move-beginning-of-line (arg)
5894 "Move point to beginning of current line as displayed.
5895\(This disregards invisible newlines such as those
5896which are part of the text that an image rests on.)
5897
5898With argument ARG not nil or 1, move forward ARG - 1 lines first.
5899If point reaches the beginning or end of buffer, it stops there.
4034b0e2 5900To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
96b83743
EZ
5901 (interactive "p")
5902 (or arg (setq arg 1))
5903 (if (/= arg 1)
5904 (condition-case nil (line-move (1- arg)) (error nil)))
5905
8570ce80
SM
5906 ;; Move to beginning-of-line, ignoring fields and invisibles.
5907 (skip-chars-backward "^\n")
5908 (while (and (not (bobp)) (line-move-invisible-p (1- (point))))
5909 (goto-char (if (featurep 'xemacs)
5910 (previous-property-change (point))
5911 (previous-char-property-change (point))))
5912 (skip-chars-backward "^\n"))
5913 (vertical-motion 0))
96b83743
EZ
5914)
5915;;;_ > move-end-of-line if necessary - older emacs, xemacs
5916(if (not (fboundp 'move-end-of-line))
5917 (defun move-end-of-line (arg)
5918 "Move point to end of current line as displayed.
5919\(This disregards invisible newlines such as those
5920which are part of the text that an image rests on.)
5921
5922With argument ARG not nil or 1, move forward ARG - 1 lines first.
5923If point reaches the beginning or end of buffer, it stops there.
4034b0e2 5924To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
96b83743
EZ
5925 (interactive "p")
5926 (or arg (setq arg 1))
8570ce80 5927 (let (done)
96b83743
EZ
5928 (while (not done)
5929 (let ((newpos
5930 (save-excursion
5931 (let ((goal-column 0))
5932 (and (condition-case nil
5933 (or (line-move arg) t)
5934 (error nil))
5935 (not (bobp))
5936 (progn
8570ce80
SM
5937 (while (and (not (bobp))
5938 (line-move-invisible-p (1- (point))))
5939 (goto-char
5940 (previous-char-property-change (point))))
96b83743
EZ
5941 (backward-char 1)))
5942 (point)))))
5943 (goto-char newpos)
5944 (if (and (> (point) newpos)
5945 (eq (preceding-char) ?\n))
5946 (backward-char 1)
5947 (if (and (> (point) newpos) (not (eobp))
5948 (not (eq (following-char) ?\n)))
5949 ;; If we skipped something intangible
5950 ;; and now we're not really at eol,
5951 ;; keep going.
5952 (setq arg 1)
8570ce80 5953 (setq done t)))))))
96b83743
EZ
5954 )
5955;;;_ > line-move-invisible-p if necessary
5956(if (not (fboundp 'line-move-invisible-p))
5957 (defun line-move-invisible-p (pos)
5958 "Return non-nil if the character after POS is currently invisible."
5959 (let ((prop
5960 (get-char-property pos 'invisible)))
5961 (if (eq buffer-invisibility-spec t)
5962 prop
5963 (or (memq prop buffer-invisibility-spec)
5964 (assq prop buffer-invisibility-spec))))))
5965
96b83743 5966;;;_ #10 Unfinished
fd5359c6
MR
5967;;;_ > allout-bullet-isearch (&optional bullet)
5968(defun allout-bullet-isearch (&optional bullet)
9179616f 5969 "Isearch \(regexp) for topic with bullet BULLET."
c567ac01
RS
5970 (interactive)
5971 (if (not bullet)
5972 (setq bullet (solicit-char-in-string
5973 "ISearch for topic with bullet: "
fd5359c6 5974 (regexp-sans-escapes allout-bullets-string))))
353e2ef2 5975
c567ac01
RS
5976 (let ((isearch-regexp t)
5977 (isearch-string (concat "^"
fd5359c6 5978 allout-header-prefix
c567ac01
RS
5979 "[ \t]*"
5980 bullet)))
5981 (isearch-repeat 'forward)
5982 (isearch-mode t)))
96b83743 5983
01fc9422
EZ
5984;;;_ #11 Unit tests - this should be last item before "Provide"
5985;;;_ > allout-run-unit-tests ()
5986(defun allout-run-unit-tests ()
5987 "Run the various allout unit tests."
5988 (message "Running allout tests...")
5989 (allout-test-resumptions)
5990 (message "Running allout tests... Done.")
5991 (sit-for .5))
5992;;;_ : test resumptions:
5993;;;_ > allout-tests-obliterate-variable (name)
5994(defun allout-tests-obliterate-variable (name)
5995 "Completely unbind variable with NAME."
5996 (if (local-variable-p name) (kill-local-variable name))
5997 (while (boundp name) (makunbound name)))
5998;;;_ > allout-test-resumptions ()
5999(defvar allout-tests-globally-unbound nil
6000 "Fodder for allout resumptions tests - defvar just for byte compiler.")
6001(defvar allout-tests-globally-true nil
6002 "Fodder for allout resumptions tests - defvar just just for byte compiler.")
6003(defvar allout-tests-locally-true nil
6004 "Fodder for allout resumptions tests - defvar just for byte compiler.")
6005(defun allout-test-resumptions ()
6006 "Exercise allout resumptions."
6007 ;; for each resumption case, we also test that the right local/global
6008 ;; scopes are affected during resumption effects:
6009
6010 ;; ensure that previously unbound variables return to the unbound state.
6011 (with-temp-buffer
6012 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6013 (allout-add-resumptions '(allout-tests-globally-unbound t))
6014 (assert (not (default-boundp 'allout-tests-globally-unbound)))
6015 (assert (local-variable-p 'allout-tests-globally-unbound))
6016 (assert (boundp 'allout-tests-globally-unbound))
6017 (assert (equal allout-tests-globally-unbound t))
6018 (allout-do-resumptions)
6019 (assert (not (local-variable-p 'allout-tests-globally-unbound)))
6020 (assert (not (boundp 'allout-tests-globally-unbound))))
6021
6022 ;; ensure that variable with prior global value is resumed
6023 (with-temp-buffer
6024 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6025 (setq allout-tests-globally-true t)
6026 (allout-add-resumptions '(allout-tests-globally-true nil))
6027 (assert (equal (default-value 'allout-tests-globally-true) t))
6028 (assert (local-variable-p 'allout-tests-globally-true))
6029 (assert (equal allout-tests-globally-true nil))
6030 (allout-do-resumptions)
6031 (assert (not (local-variable-p 'allout-tests-globally-true)))
6032 (assert (boundp 'allout-tests-globally-true))
6033 (assert (equal allout-tests-globally-true t)))
6034
6035 ;; ensure that prior local value is resumed
6036 (with-temp-buffer
6037 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6038 (set (make-local-variable 'allout-tests-locally-true) t)
6039 (assert (not (default-boundp 'allout-tests-locally-true))
6040 nil (concat "Test setup mistake - variable supposed to"
6041 " not have global binding, but it does."))
6042 (assert (local-variable-p 'allout-tests-locally-true)
6043 nil (concat "Test setup mistake - variable supposed to have"
6044 " local binding, but it lacks one."))
6045 (allout-add-resumptions '(allout-tests-locally-true nil))
6046 (assert (not (default-boundp 'allout-tests-locally-true)))
6047 (assert (local-variable-p 'allout-tests-locally-true))
6048 (assert (equal allout-tests-locally-true nil))
6049 (allout-do-resumptions)
6050 (assert (boundp 'allout-tests-locally-true))
6051 (assert (local-variable-p 'allout-tests-locally-true))
6052 (assert (equal allout-tests-locally-true t))
6053 (assert (not (default-boundp 'allout-tests-locally-true))))
6054
6055 ;; ensure that last of multiple resumptions holds, for various scopes.
6056 (with-temp-buffer
6057 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6058 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6059 (setq allout-tests-globally-true t)
6060 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6061 (set (make-local-variable 'allout-tests-locally-true) t)
6062 (allout-add-resumptions '(allout-tests-globally-unbound t)
6063 '(allout-tests-globally-true nil)
6064 '(allout-tests-locally-true nil))
6065 (allout-add-resumptions '(allout-tests-globally-unbound 2)
6066 '(allout-tests-globally-true 3)
6067 '(allout-tests-locally-true 4))
6068 ;; reestablish many of the basic conditions are maintained after re-add:
6069 (assert (not (default-boundp 'allout-tests-globally-unbound)))
6070 (assert (local-variable-p 'allout-tests-globally-unbound))
6071 (assert (equal allout-tests-globally-unbound 2))
6072 (assert (default-boundp 'allout-tests-globally-true))
6073 (assert (local-variable-p 'allout-tests-globally-true))
6074 (assert (equal allout-tests-globally-true 3))
6075 (assert (not (default-boundp 'allout-tests-locally-true)))
6076 (assert (local-variable-p 'allout-tests-locally-true))
6077 (assert (equal allout-tests-locally-true 4))
6078 (allout-do-resumptions)
6079 (assert (not (local-variable-p 'allout-tests-globally-unbound)))
6080 (assert (not (boundp 'allout-tests-globally-unbound)))
6081 (assert (not (local-variable-p 'allout-tests-globally-true)))
6082 (assert (boundp 'allout-tests-globally-true))
6083 (assert (equal allout-tests-globally-true t))
6084 (assert (boundp 'allout-tests-locally-true))
6085 (assert (local-variable-p 'allout-tests-locally-true))
6086 (assert (equal allout-tests-locally-true t))
6087 (assert (not (default-boundp 'allout-tests-locally-true))))
6088
6089 ;; ensure that deliberately unbinding registered variables doesn't foul things
6090 (with-temp-buffer
6091 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6092 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6093 (setq allout-tests-globally-true t)
6094 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6095 (set (make-local-variable 'allout-tests-locally-true) t)
6096 (allout-add-resumptions '(allout-tests-globally-unbound t)
6097 '(allout-tests-globally-true nil)
6098 '(allout-tests-locally-true nil))
6099 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6100 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6101 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6102 (allout-do-resumptions))
6103 )
6104;;;_ % Run unit tests if `allout-run-unit-tests-after-load' is true:
6105(when allout-run-unit-tests-on-load
6106 (allout-run-unit-tests))
6107
6108;;;_ #12 Provide
96b83743 6109(provide 'allout)
1977b8f6
RS
6110
6111;;;_* Local emacs vars.
31468dcb
SM
6112;; The following `allout-layout' local variable setting:
6113;; - closes all topics from the first topic to just before the third-to-last,
6114;; - shows the children of the third to last (config vars)
6115;; - and the second to last (code section),
6116;; - and closes the last topic (this local-variables section).
6117;;Local variables:
6118;;allout-layout: (0 : -1 -1 0)
6119;;End:
6120
6121;; arch-tag: cf38fbc3-c044-450f-8bff-afed8ba5681c
6a05d05f 6122;;; allout.el ends here