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