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