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