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