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