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