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