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