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