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