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