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