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