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