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