evaluation time changes
[bpt/emacs.git] / lisp / allout.el
CommitLineData
6a05d05f 1;;; allout.el --- extensive outline mode for use alone and with other modes
19b84ba3 2
ba318903 3;; Copyright (C) 1992-1994, 2001-2014 Free Software Foundation, Inc.
1977b8f6 4
8642c216
KM
5;; Author: Ken Manheimer <ken dot manheimer at gmail...>
6;; Maintainer: Ken Manheimer <ken dot manheimer at gmail...>
60c7bbd2 7;; Created: Dec 1991 -- first release to usenet
0af8c88b 8;; Version: 2.3
96497653 9;; Keywords: outlines, wp, languages, PGP, GnuPG
8642c216 10;; Website: http://myriadicity.net/software-and-systems/craft/emacs-allout
1977b8f6
RS
11
12;; This file is part of GNU Emacs.
13
eb3fa2cf 14;; GNU Emacs is free software: you can redistribute it and/or modify
59243403 15;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
59243403 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
eb3fa2cf 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
1977b8f6 26
6a05d05f 27;;; Commentary:
19b84ba3 28
96b83743
EZ
29;; Allout outline minor mode provides extensive outline formatting and
30;; and manipulation beyond standard emacs outline mode. Some features:
71296446 31;;
96b83743
EZ
32;; - Classic outline-mode topic-oriented navigation and exposure adjustment
33;; - Topic-oriented editing including coherent topic and subtopic
34;; creation, promotion, demotion, cut/paste across depths, etc.
35;; - Incremental search with dynamic exposure and reconcealment of text
60c7bbd2 36;; - Customizable bullet format -- enables programming-language specific
96b83743 37;; outlining, for code-folding editing. (Allout code itself is to try it;
60c7bbd2 38;; formatted as an outline -- do ESC-x eval-buffer in allout.el; but
96b83743 39;; emacs local file variables need to be enabled when the
60c7bbd2 40;; file was visited -- see `enable-local-variables'.)
96b83743 41;; - Configurable per-file initial exposure settings
3a00a363
KM
42;; - Symmetric-key and key-pair topic encryption. Encryption is via the
43;; Emacs 'epg' library. See allout-toggle-current-subtree-encryption
44;; docstring.
96b83743
EZ
45;; - Automatic topic-number maintenance
46;; - "Hot-spot" operation, for single-keystroke maneuvering and
d82979ea 47;; exposure control (see the allout-mode docstring)
96b83743 48;; - Easy rendering of exposed portions into numbered, latex, indented, etc
d82979ea 49;; outline styles
60c7bbd2 50;; - Careful attention to whitespace -- enabling blank lines between items
96b83743
EZ
51;; and maintenance of hanging indentation (in paragraph auto-fill and
52;; across topic promotion and demotion) of topic bodies consistent with
53;; indentation of their topic header.
71296446 54;;
d82979ea 55;; and more.
71296446 56;;
96b83743 57;; See the `allout-mode' function's docstring for an introduction to the
01fc9422
EZ
58;; mode.
59;;
96497653
KM
60;; Directions to the latest development version and helpful notes are
61;; available at http://myriadicity.net/Sundry/EmacsAllout .
96b83743 62;;
11c46e15
KM
63;; The outline menubar additions provide quick reference to many of the
64;; features. See the docstring of the variables `allout-layout' and
fd5359c6 65;; `allout-auto-activation' for details on automatic activation of
11c46e15
KM
66;; `allout-mode' as a minor mode. (`allout-init' is deprecated in favor of
67;; a purely customization-based method.)
c567ac01 68;;
60c7bbd2 69;; Note -- the lines beginning with `;;;_' are outline topic headers.
11c46e15
KM
70;; Customize `allout-auto-activation' to enable, then revisit this
71;; buffer to give it a whirl.
19b84ba3 72
d82979ea 73;; ken manheimer (ken dot manheimer at gmail dot com)
c567ac01 74
6a05d05f
PJ
75;;; Code:
76
9d3aa82c
JB
77(declare-function epa-passphrase-callback-function
78 "epa" (context key-id handback))
79
96497653 80;;;_* Dependency loads
96b83743 81(require 'overlay)
01fc9422 82(eval-when-compile
08cf18e4
GM
83 ;; `cl' is required for `assert'. `assert' is not covered by a standard
84 ;; autoload, but it is a macro, so that eval-when-compile is sufficient
85 ;; to byte-compile it in, or to do the require when the buffer evalled.
86 (require 'cl)
87 )
d82979ea 88
19b84ba3 89;;;_* USER CUSTOMIZATION VARIABLES:
96b83743 90
1c9b9df0 91;;;_ > defgroup allout, allout-keybindings
4bef9110 92(defgroup allout nil
96497653
KM
93 "Extensive outline minor-mode, for use stand-alone and with other modes.
94
95See Allout Auto Activation for automatic activation."
fd5359c6 96 :prefix "allout-"
d82979ea 97 :group 'outlines)
1c9b9df0
KM
98(defgroup allout-keybindings nil
99 "Allout outline mode keyboard bindings configuration."
100 :group 'allout)
19b84ba3
RS
101
102;;;_ + Layout, Mode, and Topic Header Configuration
103
08b5f072 104(defvar allout-command-prefix) ; defined below
08b5f072 105
1c9b9df0 106;;;_ > allout-keybindings incidentals:
d3e51865
KM
107;;;_ : internal key binding stuff - in this section for load-order.
108;;;_ = allout-mode-map
109(defvar allout-mode-map 'allout-mode-map
110 "Keybindings place-holder for (allout) outline minor mode.
111
112Do NOT set the value of this variable. Instead, customize
113`allout-command-prefix', `allout-prefixed-keybindings', and
114`allout-unprefixed-keybindings'.")
115;;;_ = allout-mode-map-value
116(defvar allout-mode-map-value nil
117 "Keymap for allout outline minor mode.
118
119Do NOT set the value of this variable. Instead, customize
120`allout-command-prefix', `allout-prefixed-keybindings', and
121`allout-unprefixed-keybindings'.")
122;;;_ = make allout-mode-map-value an alias for allout-mode-map:
123;; this needs to be revised when the value is changed, sigh.
124(defalias 'allout-mode-map allout-mode-map-value)
125;;;_ > allout-compose-and-institute-keymap (&optional varname value)
126(defun allout-compose-and-institute-keymap (&optional varname value)
127 "Create the allout keymap according to the keybinding specs, and set it.
128
129Useful standalone or to effect customizations of the
1c9b9df0
KM
130respective allout-mode keybinding variables, `allout-command-prefix',
131`allout-prefixed-keybindings', and `allout-unprefixed-keybindings'"
132 ;; Set the customization variable, if any:
133 (when varname
134 (set-default varname value))
d3e51865 135 (let ((map (make-sparse-keymap)))
1c9b9df0 136 (when (boundp 'allout-prefixed-keybindings)
d3e51865 137 ;; tolerate first definitions of the variables:
1c9b9df0
KM
138 (dolist (entry allout-prefixed-keybindings)
139 (define-key map
140 ;; XXX vector vs non-vector key descriptions?
141 (vconcat allout-command-prefix
142 (car (read-from-string (car entry))))
143 (cadr entry))))
144 (when (boundp 'allout-unprefixed-keybindings)
145 (dolist (entry allout-unprefixed-keybindings)
146 (define-key map (car (read-from-string (car entry))) (cadr entry))))
d3e51865
KM
147 (substitute-key-definition 'beginning-of-line 'allout-beginning-of-line
148 map global-map)
149 (substitute-key-definition 'move-beginning-of-line 'allout-beginning-of-line
150 map global-map)
151 (substitute-key-definition 'end-of-line 'allout-end-of-line
152 map global-map)
153 (substitute-key-definition 'move-end-of-line 'allout-end-of-line
154 map global-map)
155 (allout-institute-keymap map)))
156;;;_ > allout-institute-keymap (map)
157(defun allout-institute-keymap (map)
158 "Associate allout-mode bindings with allout as a minor mode."
159 ;; Architecture:
160 ;; allout-mode-map var is a keymap by virtue of being a defalias for
161 ;; allout-mode-map-value, which has the actual keymap value.
162 ;; allout-mode-map's symbol value is just 'allout-mode-map, so it can be
163 ;; used in minor-mode-map-alist to indirect to the actual
164 ;; allout-mode-map-var value, which can be adjusted and reassigned.
165
642f3c5c 166 ;; allout-mode-map-value for keymap reference in various places:
d3e51865 167 (setq allout-mode-map-value map)
642f3c5c
KM
168 ;; the function value keymap of allout-mode-map is used in
169 ;; minor-mode-map-alist - update it:
170 (fset allout-mode-map allout-mode-map-value))
cd1181db 171;;;_ * initialize the mode map:
d3e51865
KM
172;; ensure that allout-mode-map has some setting even if allout-mode hasn't
173;; been invoked:
174(allout-compose-and-institute-keymap)
1c9b9df0 175;;;_ = allout-command-prefix
2a1408fd 176(defcustom allout-command-prefix "\C-c "
9201cc28 177 "Key sequence to be used as prefix for outline mode command key bindings.
2a1408fd
DN
178
179Default is '\C-c<space>'; just '\C-c' is more short-and-sweet, if you're
180willing to let allout use a bunch of \C-c keybindings."
181 :type 'string
1c9b9df0 182 :group 'allout-keybindings
d3e51865 183 :set 'allout-compose-and-institute-keymap)
1c9b9df0
KM
184;;;_ = allout-keybindings-binding
185(define-widget 'allout-keybindings-binding 'lazy
186 "Structure of allout keybindings customization items."
187 :type '(repeat
188 (list (string :tag "Key" :value "[(meta control shift ?f)]")
189 (function :tag "Function name"
190 :value allout-forward-current-level))))
191;;;_ = allout-prefixed-keybindings
192(defcustom allout-prefixed-keybindings
193 '(("[(control ?n)]" allout-next-visible-heading)
194 ("[(control ?p)]" allout-previous-visible-heading)
d3e51865 195 ("[(control ?u)]" allout-up-current-level)
1c9b9df0
KM
196 ("[(control ?f)]" allout-forward-current-level)
197 ("[(control ?b)]" allout-backward-current-level)
198 ("[(control ?a)]" allout-beginning-of-current-entry)
199 ("[(control ?e)]" allout-end-of-entry)
200 ("[(control ?i)]" allout-show-children)
1c9b9df0
KM
201 ("[(control ?s)]" allout-show-current-subtree)
202 ("[(control ?t)]" allout-toggle-current-subtree-exposure)
d3e51865
KM
203;; Let user customize if they want to preempt describe-prefix-bindings ^h use.
204;; ("[(control ?h)]" allout-hide-current-subtree)
1c9b9df0
KM
205 ("[?h]" allout-hide-current-subtree)
206 ("[(control ?o)]" allout-show-current-entry)
207 ("[?!]" allout-show-all)
208 ("[?x]" allout-toggle-current-subtree-encryption)
209 ("[? ]" allout-open-sibtopic)
210 ("[?.]" allout-open-subtopic)
211 ("[?,]" allout-open-supertopic)
212 ("[?']" allout-shift-in)
213 ("[?>]" allout-shift-in)
214 ("[?<]" allout-shift-out)
215 ("[(control ?m)]" allout-rebullet-topic)
216 ("[?*]" allout-rebullet-current-heading)
4936e3ba 217 ("[?#]" allout-number-siblings)
1c9b9df0 218 ("[(control ?k)]" allout-kill-topic)
d3e51865 219 ("[(meta ?k)]" allout-copy-topic-as-kill)
1c9b9df0
KM
220 ("[?@]" allout-resolve-xref)
221 ("[?=?c]" allout-copy-exposed-to-buffer)
222 ("[?=?i]" allout-indented-exposed-to-buffer)
223 ("[?=?t]" allout-latexify-exposed)
224 ("[?=?p]" allout-flatten-exposed-to-buffer)
225 )
226 "Allout-mode key bindings that are prefixed with `allout-command-prefix'.
227
228See `allout-unprefixed-keybindings' for the list of keybindings
229that are not prefixed.
230
231Use vector format for the keys:
232 - put literal keys after a '?' question mark, eg: '?a', '?.'
233 - enclose control, shift, or meta-modified keys as sequences within
234 parentheses, with the literal key, as above, preceded by the name(s)
01c35094 235 of the modifiers, eg: [(control ?a)]
1c9b9df0
KM
236See the existing keys for examples.
237
238Functions can be bound to multiple keys, but binding keys to
239multiple functions will not work - the last binding for a key
240prevails."
2bed3f04 241 :version "24.1"
1c9b9df0
KM
242 :type 'allout-keybindings-binding
243 :group 'allout-keybindings
d3e51865 244 :set 'allout-compose-and-institute-keymap
1c9b9df0
KM
245 )
246;;;_ = allout-unprefixed-keybindings
247(defcustom allout-unprefixed-keybindings
248 '(("[(control ?k)]" allout-kill-line)
d3e51865 249 ("[(meta ?k)]" allout-copy-line-as-kill)
1c9b9df0 250 ("[(control ?y)]" allout-yank)
d3e51865 251 ("[(meta ?y)]" allout-yank-pop)
1c9b9df0
KM
252 )
253 "Allout-mode functions bound to keys without any added prefix.
254
255This is in contrast to the majority of allout-mode bindings on
256`allout-prefixed-bindings', whose bindings are created with a
0d327994 257preceding command key.
1c9b9df0
KM
258
259Use vector format for the keys:
260 - put literal keys after a '?' question mark, eg: '?a', '?.'
261 - enclose control, shift, or meta-modified keys as sequences within
262 parentheses, with the literal key, as above, preceded by the name(s)
01c35094 263 of the modifiers, eg: [(control ?a)]
1c9b9df0 264See the existing keys for examples."
2bed3f04 265 :version "24.1"
1c9b9df0
KM
266 :type 'allout-keybindings-binding
267 :group 'allout-keybindings
d3e51865 268 :set 'allout-compose-and-institute-keymap
1c9b9df0
KM
269 )
270
11c46e15
KM
271;;;_ > allout-auto-activation-helper (var value)
272;;;###autoload
273(defun allout-auto-activation-helper (var value)
274 "Institute `allout-auto-activation'.
275
276Intended to be used as the `allout-auto-activation' :set function."
277 (set-default var value)
278 (allout-setup))
279;;;_ > allout-setup ()
280;;;###autoload
281(defun allout-setup ()
20db1522 282 "Do fundamental Emacs session for allout auto-activation.
11c46e15
KM
283
284Establishes allout processing as part of visiting a file if
285`allout-auto-activation' is non-nil, or removes it otherwise.
286
287The proper way to use this is through customizing the setting of
288`allout-auto-activation'."
289 (if (not allout-auto-activation)
290 (remove-hook 'find-file-hook 'allout-find-file-hook)
291 (add-hook 'find-file-hook 'allout-find-file-hook)))
fd5359c6 292;;;_ = allout-auto-activation
11c46e15 293;;;###autoload
fd5359c6 294(defcustom allout-auto-activation nil
11c46e15 295 "Configure allout outline mode auto-activation.
19b84ba3 296
11c46e15
KM
297Control whether and how allout outline mode is automatically
298activated when files are visited with non-nil buffer-specific
299file variable `allout-layout'.
19b84ba3 300
20db1522 301When allout-auto-activation is \"On\" (t), allout mode is
11c46e15
KM
302activated in buffers with non-nil `allout-layout', and the
303specified layout is applied.
19b84ba3 304
11c46e15 305With value \"ask\", auto-mode-activation is enabled, and endorsement for
19b84ba3
RS
306performing auto-layout is asked of the user each time.
307
11c46e15
KM
308With value \"activate\", only auto-mode-activation is enabled.
309Auto-layout is not.
19b84ba3 310
96497653 311With value nil, inhibit any automatic allout-mode activation."
11c46e15 312 :set 'allout-auto-activation-helper
c6eee9aa 313 ;; FIXME: Using strings here is unusual and less efficient than symbols.
9179616f
DL
314 :type '(choice (const :tag "On" t)
315 (const :tag "Ask about layout" "ask")
316 (const :tag "Mode only" "activate")
317 (const :tag "Off" nil))
318 :group 'allout)
11c46e15 319(allout-setup)
4034b0e2
LT
320;;;_ = allout-default-layout
321(defcustom allout-default-layout '(-2 : 0)
9201cc28 322 "Default allout outline layout specification.
4034b0e2
LT
323
324This setting specifies the outline exposure to use when
325`allout-layout' has the local value `t'. This docstring describes the
326layout specifications.
327
328A list value specifies a default layout for the current buffer,
329to be applied upon activation of `allout-mode'. Any non-nil
330value will automatically trigger `allout-mode', provided
11c46e15 331`allout-auto-activation' has been customized to enable it.
4034b0e2
LT
332
333The types of elements in the layout specification are:
334
60c7bbd2
RS
335 INTEGER -- dictate the relative depth to open the corresponding topic(s),
336 where:
337 -- negative numbers force the topic to be closed before opening
338 to the absolute value of the number, so all siblings are open
339 only to that level.
340 -- positive numbers open to the relative depth indicated by the
341 number, but do not force already opened subtopics to be closed.
342 -- 0 means to close topic -- hide all subitems.
97610156 343 : -- repeat spec -- apply the preceding element to all siblings at
60c7bbd2
RS
344 current level, *up to* those siblings that would be covered by specs
345 following the `:' on the list. Ie, apply to all topics at level but
346 trailing ones accounted for by trailing specs. (Only the first of
347 multiple colons at the same level is honored -- later ones are ignored.)
348 * -- completely exposes the topic, including bodies
349 + -- exposes all subtopics, but not the bodies
350 - -- exposes the body of the corresponding topic, but not subtopics
351 LIST -- a nested layout spec, to be applied intricately to its
4034b0e2 352 corresponding item(s)
1977b8f6 353
4034b0e2 354Examples:
60c7bbd2 355 (-2 : 0)
4034b0e2
LT
356 Collapse the top-level topics to show their children and
357 grandchildren, but completely collapse the final top-level topic.
60c7bbd2 358 (-1 () : 1 0)
4034b0e2
LT
359 Close the first topic so only the immediate subtopics are shown,
360 leave the subsequent topics exposed as they are until the second
361 second to last topic, which is exposed at least one level, and
362 completely close the last topic.
60c7bbd2 363 (-2 : -1 *)
4034b0e2
LT
364 Expose children and grandchildren of all topics at current
365 level except the last two; expose children of the second to
366 last and completely expose the last one, including its subtopics.
19b84ba3 367
4034b0e2 368See `allout-expose-topic' for more about the exposure process.
19b84ba3 369
353e2ef2
KH
370Also, allout's mode-specific provisions will make topic prefixes default
371to the comment-start string, if any, of the language of the file. This
4034b0e2
LT
372is modulo the setting of `allout-use-mode-specific-leader', which see."
373 :type 'allout-layout-type
374 :group 'allout)
375;;;_ : allout-layout-type
376(define-widget 'allout-layout-type 'lazy
377 "Allout layout format customization basic building blocks."
378 :type '(repeat
379 (choice (integer :tag "integer (<= zero is strict)")
380 (const :tag ": (repeat prior)" :)
381 (const :tag "* (completely expose)" *)
382 (const :tag "+ (expose all offspring, headlines only)" +)
383 (const :tag "- (expose topic body but not offspring)" -)
384 (allout-layout-type :tag "<Nested layout>"))))
385
2a1408fd
DN
386;;;_ = allout-inhibit-auto-fill
387(defcustom allout-inhibit-auto-fill nil
9201cc28 388 "If non-nil, auto-fill will be inhibited in the allout buffers.
2a1408fd
DN
389
390You can customize this setting to set it for all allout buffers, or set it
391in individual buffers if you want to inhibit auto-fill only in particular
392buffers. (You could use a function on `allout-mode-hook' to inhibit
393auto-fill according, eg, to the major mode.)
394
395If you don't set this and auto-fill-mode is enabled, allout will use the
396value that `normal-auto-fill-function', if any, when allout mode starts, or
397else allout's special hanging-indent maintaining auto-fill function,
398`allout-auto-fill'."
399 :type 'boolean
400 :group 'allout)
401(make-variable-buffer-local 'allout-inhibit-auto-fill)
381987c3
KM
402;;;_ = allout-inhibit-auto-fill-on-headline
403(defcustom allout-inhibit-auto-fill-on-headline nil
404 "If non-nil, auto-fill will be inhibited while on topic's header line."
2bed3f04 405 :version "24.1"
381987c3
KM
406 :type 'boolean
407 :group 'allout)
408(make-variable-buffer-local 'allout-inhibit-auto-fill-on-headline)
2a1408fd
DN
409;;;_ = allout-use-hanging-indents
410(defcustom allout-use-hanging-indents t
9201cc28 411 "If non-nil, topic body text auto-indent defaults to indent of the header.
2a1408fd 412Ie, it is indented to be just past the header prefix. This is
8dd59f01 413relevant mostly for use with `indented-text-mode', or other situations
2a1408fd
DN
414where auto-fill occurs."
415 :type 'boolean
416 :group 'allout)
417(make-variable-buffer-local 'allout-use-hanging-indents)
418;;;###autoload
419(put 'allout-use-hanging-indents 'safe-local-variable
4f91a816 420 (if (fboundp 'booleanp) 'booleanp (lambda (x) (member x '(t nil)))))
2a1408fd
DN
421;;;_ = allout-reindent-bodies
422(defcustom allout-reindent-bodies (if allout-use-hanging-indents
423 'text)
9201cc28 424 "Non-nil enables auto-adjust of topic body hanging indent with depth shifts.
2a1408fd
DN
425
426When active, topic body lines that are indented even with or beyond
427their topic header are reindented to correspond with depth shifts of
428the header.
429
430A value of t enables reindent in non-programming-code buffers, ie
431those that do not have the variable `comment-start' set. A value of
432`force' enables reindent whether or not `comment-start' is set."
433 :type '(choice (const nil) (const t) (const text) (const force))
434 :group 'allout)
435
436(make-variable-buffer-local 'allout-reindent-bodies)
437;;;###autoload
438(put 'allout-reindent-bodies 'safe-local-variable
4f91a816 439 (lambda (x) (memq x '(nil t text force))))
2a1408fd 440
fd5359c6
MR
441;;;_ = allout-show-bodies
442(defcustom allout-show-bodies nil
9201cc28 443 "If non-nil, show entire body when exposing a topic, rather than
9179616f
DL
444just the header."
445 :type 'boolean
446 :group 'allout)
fd5359c6 447(make-variable-buffer-local 'allout-show-bodies)
8570ce80
SM
448;;;###autoload
449(put 'allout-show-bodies 'safe-local-variable
4f91a816 450 (if (fboundp 'booleanp) 'booleanp (lambda (x) (member x '(t nil)))))
1977b8f6 451
48bd8440
EZ
452;;;_ = allout-beginning-of-line-cycles
453(defcustom allout-beginning-of-line-cycles t
9201cc28 454 "If non-nil, \\[allout-beginning-of-line] will cycle through smart-placement options.
48bd8440
EZ
455
456Cycling only happens on when the command is repeated, not when it
457follows a different command.
458
459Smart-placement means that repeated calls to this function will
460advance as follows:
461
462 - if the cursor is on a non-headline body line and not on the first column:
463 then it goes to the first column
464 - if the cursor is on the first column of a non-headline body line:
465 then it goes to the start of the headline within the item body
466 - if the cursor is on the headline and not the start of the headline:
467 then it goes to the start of the headline
468 - if the cursor is on the start of the headline:
c07583cd 469 then it goes to the bullet character (for hotspot navigation)
48bd8440 470 - if the cursor is on the bullet character:
c07583cd 471 then it goes to the first column of that line (the headline)
48bd8440
EZ
472 - if the cursor is on the first column of the headline:
473 then it goes to the start of the headline within the item body.
474
475In this fashion, you can use the beginning-of-line command to do
476its normal job and then, when repeated, advance through the
477entry, cycling back to start.
478
479If this configuration variable is nil, then the cursor is just
480advanced to the beginning of the line and remains there on
481repeated calls."
482 :type 'boolean :group 'allout)
483;;;_ = allout-end-of-line-cycles
484(defcustom allout-end-of-line-cycles t
9201cc28 485 "If non-nil, \\[allout-end-of-line] will cycle through smart-placement options.
48bd8440
EZ
486
487Cycling only happens on when the command is repeated, not when it
488follows a different command.
489
60c7bbd2 490Smart placement means that repeated calls to this function will
48bd8440
EZ
491advance as follows:
492
493 - if the cursor is not on the end-of-line,
494 then it goes to the end-of-line
495 - if the cursor is on the end-of-line but not the end-of-entry,
496 then it goes to the end-of-entry, exposing it if necessary
497 - if the cursor is on the end-of-entry,
498 then it goes to the end of the head line
499
500In this fashion, you can use the end-of-line command to do its
501normal job and then, when repeated, advance through the entry,
502cycling back to start.
503
504If this configuration variable is nil, then the cursor is just
505advanced to the end of the line and remains there on repeated
506calls."
507 :type 'boolean :group 'allout)
508
fd5359c6
MR
509;;;_ = allout-header-prefix
510(defcustom allout-header-prefix "."
48bd8440
EZ
511;; this string is treated as literal match. it will be `regexp-quote'd, so
512;; one cannot use regular expressions to match varying header prefixes.
9201cc28 513 "Leading string which helps distinguish topic headers.
19b84ba3
RS
514
515Outline topic header lines are identified by a leading topic
c567ac01 516header prefix, which mostly have the value of this var at their front.
48bd8440
EZ
517Level 1 topics are exceptions. They consist of only a single
518character, which is typically set to the `allout-primary-bullet'."
4bef9110
SE
519 :type 'string
520 :group 'allout)
fd5359c6 521(make-variable-buffer-local 'allout-header-prefix)
8570ce80
SM
522;;;###autoload
523(put 'allout-header-prefix 'safe-local-variable 'stringp)
fd5359c6
MR
524;;;_ = allout-primary-bullet
525(defcustom allout-primary-bullet "*"
19b84ba3
RS
526 "Bullet used for top-level outline topics.
527
528Outline topic header lines are identified by a leading topic header
c567ac01 529prefix, which is concluded by bullets that includes the value of this
fd5359c6 530var and the respective allout-*-bullets-string vars.
c567ac01 531
353e2ef2 532The value of an asterisk (`*') provides for backwards compatibility
e126900f 533with the original Emacs outline mode. See `allout-plain-bullets-string'
2265e017 534and `allout-distinctive-bullets-string' for the range of available
4bef9110
SE
535bullets."
536 :type 'string
537 :group 'allout)
fd5359c6 538(make-variable-buffer-local 'allout-primary-bullet)
8570ce80
SM
539;;;###autoload
540(put 'allout-primary-bullet 'safe-local-variable 'stringp)
fd5359c6 541;;;_ = allout-plain-bullets-string
d82979ea 542(defcustom allout-plain-bullets-string ".,"
9201cc28 543 "The bullets normally used in outline topic prefixes.
19b84ba3 544
fd5359c6 545See `allout-distinctive-bullets-string' for the other kind of
c567ac01 546bullets.
1977b8f6 547
353e2ef2 548DO NOT include the close-square-bracket, `]', as a bullet.
1977b8f6 549
c567ac01 550Outline mode has to be reactivated in order for changes to the value
4bef9110
SE
551of this var to take effect."
552 :type 'string
553 :group 'allout)
fd5359c6 554(make-variable-buffer-local 'allout-plain-bullets-string)
8570ce80
SM
555;;;###autoload
556(put 'allout-plain-bullets-string 'safe-local-variable 'stringp)
fd5359c6 557;;;_ = allout-distinctive-bullets-string
d82979ea 558(defcustom allout-distinctive-bullets-string "*+-=>()[{}&!?#%\"X@$~_\\:;^"
9201cc28 559 "Persistent outline header bullets used to distinguish special topics.
1977b8f6 560
38b2ca53
NR
561These bullets are distinguish topics with particular character.
562They are not used by default in the topic creation routines, but
563are offered as options when you modify topic creation with a
20db1522 564universal argument (\\[universal-argument]), or during rebulleting (\\[allout-rebullet-current-heading]).
38b2ca53
NR
565
566Distinctive bullets are not cycled when topics are shifted or
567otherwise automatically rebulleted, so their marking is
568persistent until deliberately changed. Their significance is
569purely by convention, however. Some conventions suggest
570themselves:
571
60c7bbd2
RS
572 `(' - open paren -- an aside or incidental point
573 `?' - question mark -- uncertain or outright question
574 `!' - exclamation point/bang -- emphatic
575 `[' - open square bracket -- meta-note, about item instead of item's subject
576 `\"' - double quote -- a quotation or other citation
0af8c88b 577 `=' - equal sign -- an assignment, some kind of definition
60c7bbd2 578 `^' - carat -- relates to something above
38b2ca53
NR
579
580Some are more elusive, but their rationale may be recognizable:
581
60c7bbd2
RS
582 `+' - plus -- pending consideration, completion
583 `_' - underscore -- done, completed
584 `&' - ampersand -- addendum, furthermore
38b2ca53
NR
585
586\(Some other non-plain bullets have special meaning to the
587software. By default:
588
60c7bbd2
RS
589 `~' marks encryptable topics -- see `allout-topic-encryption-bullet'
590 `#' marks auto-numbered bullets -- see `allout-numbered-bullet'.)
38b2ca53
NR
591
592See `allout-plain-bullets-string' for the standard, alternating
593bullets.
1977b8f6 594
fd5359c6 595You must run `set-allout-regexp' in order for outline mode to
38b2ca53 596adopt changes of this value.
19b84ba3 597
353e2ef2 598DO NOT include the close-square-bracket, `]', on either of the bullet
4bef9110
SE
599strings."
600 :type 'string
601 :group 'allout)
fd5359c6 602(make-variable-buffer-local 'allout-distinctive-bullets-string)
8570ce80
SM
603;;;###autoload
604(put 'allout-distinctive-bullets-string 'safe-local-variable 'stringp)
1977b8f6 605
fd5359c6
MR
606;;;_ = allout-use-mode-specific-leader
607(defcustom allout-use-mode-specific-leader t
9201cc28 608 "When non-nil, use mode-specific topic-header prefixes.
19b84ba3 609
48bd8440
EZ
610Allout outline mode will use the mode-specific `allout-mode-leaders' or
611comment-start string, if any, to lead the topic prefix string, so topic
612headers look like comments in the programming language. It will also use
613the comment-start string, with an '_' appended, for `allout-primary-bullet'.
19b84ba3 614
48bd8440 615String values are used as literals, not regular expressions, so
40ba43b4 616do not escape any regular-expression characters.
19b84ba3 617
24190da5 618Value t means to first check for assoc value in `allout-mode-leaders'
c07583cd 619alist, then use comment-start string, if any, then use default (`.').
9179616f 620\(See note about use of comment-start strings, below.)
19b84ba3 621
fd5359c6 622Set to the symbol for either of `allout-mode-leaders' or
19b84ba3
RS
623`comment-start' to use only one of them, respectively.
624
c07583cd 625Value nil means to always use the default (`.') and leave
48bd8440
EZ
626`allout-primary-bullet' unaltered.
627
628comment-start strings that do not end in spaces are tripled in
629the header-prefix, and an `_' underscore is tacked on the end, to
630distinguish them from regular comment strings. comment-start
631strings that do end in spaces are not tripled, but an underscore
75bcb912 632is substituted for the space. [This presumes that the space is
48bd8440
EZ
633for appearance, not comment syntax. You can use
634`allout-mode-leaders' to override this behavior, when
635undesired.]"
71296446 636 :type '(choice (const t) (const nil) string
fd5359c6 637 (const allout-mode-leaders)
4bef9110
SE
638 (const comment-start))
639 :group 'allout)
8570ce80
SM
640;;;###autoload
641(put 'allout-use-mode-specific-leader 'safe-local-variable
4f91a816 642 (lambda (x) (or (memq x '(t nil allout-mode-leaders comment-start))
da506f05 643 (stringp x))))
fd5359c6
MR
644;;;_ = allout-mode-leaders
645(defvar allout-mode-leaders '()
646 "Specific allout-prefix leading strings per major modes.
19b84ba3 647
48bd8440
EZ
648Use this if the mode's comment-start string isn't what you
649prefer, or if the mode lacks a comment-start string. See
650`allout-use-mode-specific-leader' for more details.
19b84ba3
RS
651
652If you're constructing a string that will comment-out outline
653structuring so it can be included in program code, append an extra
654character, like an \"_\" underscore, to distinguish the lead string
48bd8440 655from regular comments that start at the beginning-of-line.")
19b84ba3 656
fd5359c6
MR
657;;;_ = allout-old-style-prefixes
658(defcustom allout-old-style-prefixes nil
9201cc28 659 "When non-nil, use only old-and-crusty `outline-mode' `*' topic prefixes.
19b84ba3
RS
660
661Non-nil restricts the topic creation and modification
c567ac01 662functions to asterix-padded prefixes, so they look exactly
e126900f 663like the original Emacs-outline style prefixes.
1977b8f6 664
c567ac01 665Whatever the setting of this variable, both old and new style prefixes
4bef9110
SE
666are always respected by the topic maneuvering functions."
667 :type 'boolean
668 :group 'allout)
fd5359c6 669(make-variable-buffer-local 'allout-old-style-prefixes)
8570ce80
SM
670;;;###autoload
671(put 'allout-old-style-prefixes 'safe-local-variable
4f91a816 672 (if (fboundp 'booleanp) 'booleanp (lambda (x) (member x '(t nil)))))
60c7bbd2 673;;;_ = allout-stylish-prefixes -- alternating bullets
fd5359c6 674(defcustom allout-stylish-prefixes t
9201cc28 675 "Do fancy stuff with topic prefix bullets according to level, etc.
c567ac01 676
19b84ba3
RS
677Non-nil enables topic creation, modification, and repositioning
678functions to vary the topic bullet char (the char that marks the topic
679depth) just preceding the start of the topic text) according to level.
353e2ef2 680Otherwise, only asterisks (`*') and distinctive bullets are used.
19b84ba3
RS
681
682This is how an outline can look (but sans indentation) with stylish
683prefixes:
c567ac01
RS
684
685 * Top level
686 .* A topic
687 . + One level 3 subtopic
688 . . One level 4 subtopic
19b84ba3 689 . . A second 4 subtopic
c567ac01 690 . + Another level 3 subtopic
19b84ba3
RS
691 . #1 A numbered level 4 subtopic
692 . #2 Another
693 . ! Another level 4 subtopic with a different distinctive bullet
694 . #4 And another numbered level 4 subtopic
c567ac01 695
19b84ba3
RS
696This would be an outline with stylish prefixes inhibited (but the
697numbered and other distinctive bullets retained):
c567ac01
RS
698
699 * Top level
700 .* A topic
19b84ba3
RS
701 . * One level 3 subtopic
702 . * One level 4 subtopic
703 . * A second 4 subtopic
704 . * Another level 3 subtopic
705 . #1 A numbered level 4 subtopic
706 . #2 Another
707 . ! Another level 4 subtopic with a different distinctive bullet
708 . #4 And another numbered level 4 subtopic
c567ac01
RS
709
710Stylish and constant prefixes (as well as old-style prefixes) are
711always respected by the topic maneuvering functions, regardless of
712this variable setting.
713
2265e017 714The setting of this var is not relevant when `allout-old-style-prefixes'
4bef9110
SE
715is non-nil."
716 :type 'boolean
717 :group 'allout)
fd5359c6 718(make-variable-buffer-local 'allout-stylish-prefixes)
8570ce80
SM
719;;;###autoload
720(put 'allout-stylish-prefixes 'safe-local-variable
4f91a816 721 (if (fboundp 'booleanp) 'booleanp (lambda (x) (member x '(t nil)))))
1977b8f6 722
fd5359c6
MR
723;;;_ = allout-numbered-bullet
724(defcustom allout-numbered-bullet "#"
9201cc28 725 "String designating bullet of topics that have auto-numbering; nil for none.
19b84ba3 726
a0776d6b 727Topics having this bullet have automatic maintenance of a sibling
19b84ba3 728sequence-number tacked on, just after the bullet. Conventionally set
c567ac01 729to \"#\", you can set it to a bullet of your choice. A nil value
4bef9110
SE
730disables numbering maintenance."
731 :type '(choice (const nil) string)
732 :group 'allout)
fd5359c6 733(make-variable-buffer-local 'allout-numbered-bullet)
8570ce80 734;;;###autoload
64686e6d
RS
735(put 'allout-numbered-bullet 'safe-local-variable
736 (if (fboundp 'string-or-null-p)
737 'string-or-null-p
4f91a816 738 (lambda (x) (or (stringp x) (null x)))))
fd5359c6
MR
739;;;_ = allout-file-xref-bullet
740(defcustom allout-file-xref-bullet "@"
9201cc28 741 "Bullet signifying file cross-references, for `allout-resolve-xref'.
19b84ba3 742
9179616f 743Set this var to the bullet you want to use for file cross-references."
4bef9110
SE
744 :type '(choice (const nil) string)
745 :group 'allout)
8570ce80 746;;;###autoload
64686e6d
RS
747(put 'allout-file-xref-bullet 'safe-local-variable
748 (if (fboundp 'string-or-null-p)
749 'string-or-null-p
4f91a816 750 (lambda (x) (or (stringp x) (null x)))))
fd5359c6
MR
751;;;_ = allout-presentation-padding
752(defcustom allout-presentation-padding 2
9201cc28 753 "Presentation-format white-space padding factor, for greater indent."
9179616f
DL
754 :type 'integer
755 :group 'allout)
756
fd5359c6 757(make-variable-buffer-local 'allout-presentation-padding)
8570ce80
SM
758;;;###autoload
759(put 'allout-presentation-padding 'safe-local-variable 'integerp)
9179616f 760
96497653
KM
761;;;_ = allout-flattened-numbering-abbreviation
762(define-obsolete-variable-alias 'allout-abbreviate-flattened-numbering
c6eee9aa 763 'allout-flattened-numbering-abbreviation "24.1")
96497653 764(defcustom allout-flattened-numbering-abbreviation nil
9201cc28 765 "If non-nil, `allout-flatten-exposed-to-buffer' abbreviates topic
9179616f
DL
766numbers to minimal amount with some context. Otherwise, entire
767numbers are always used."
2bed3f04 768 :version "24.1"
9179616f
DL
769 :type 'boolean
770 :group 'allout)
771
c567ac01 772;;;_ + LaTeX formatting
fd5359c6
MR
773;;;_ - allout-number-pages
774(defcustom allout-number-pages nil
9201cc28 775 "Non-nil turns on page numbering for LaTeX formatting of an outline."
4bef9110
SE
776 :type 'boolean
777 :group 'allout)
fd5359c6
MR
778;;;_ - allout-label-style
779(defcustom allout-label-style "\\large\\bf"
9201cc28 780 "Font and size of labels for LaTeX formatting of an outline."
4bef9110
SE
781 :type 'string
782 :group 'allout)
fd5359c6
MR
783;;;_ - allout-head-line-style
784(defcustom allout-head-line-style "\\large\\sl "
9201cc28 785 "Font and size of entries for LaTeX formatting of an outline."
4bef9110
SE
786 :type 'string
787 :group 'allout)
fd5359c6
MR
788;;;_ - allout-body-line-style
789(defcustom allout-body-line-style " "
9201cc28 790 "Font and size of entries for LaTeX formatting of an outline."
4bef9110
SE
791 :type 'string
792 :group 'allout)
fd5359c6
MR
793;;;_ - allout-title-style
794(defcustom allout-title-style "\\Large\\bf"
9201cc28 795 "Font and size of titles for LaTeX formatting of an outline."
4bef9110
SE
796 :type 'string
797 :group 'allout)
fd5359c6 798;;;_ - allout-title
d82979ea 799(defcustom allout-title '(or buffer-file-name (buffer-name))
9201cc28 800 "Expression to be evaluated to determine the title for LaTeX
4bef9110
SE
801formatted copy."
802 :type 'sexp
803 :group 'allout)
fd5359c6
MR
804;;;_ - allout-line-skip
805(defcustom allout-line-skip ".05cm"
9201cc28 806 "Space between lines for LaTeX formatting of an outline."
4bef9110
SE
807 :type 'string
808 :group 'allout)
fd5359c6
MR
809;;;_ - allout-indent
810(defcustom allout-indent ".3cm"
9201cc28 811 "LaTeX formatted depth-indent spacing."
4bef9110
SE
812 :type 'string
813 :group 'allout)
c567ac01 814
d82979ea 815;;;_ + Topic encryption
8570ce80
SM
816;;;_ = allout-encryption group
817(defgroup allout-encryption nil
818 "Settings for topic encryption features of allout outliner."
819 :group 'allout)
d82979ea
EZ
820;;;_ = allout-topic-encryption-bullet
821(defcustom allout-topic-encryption-bullet "~"
9201cc28 822 "Bullet signifying encryption of the entry's body."
d82979ea 823 :type '(choice (const nil) string)
21e8bc99 824 :version "22.1"
8570ce80 825 :group 'allout-encryption)
d82979ea 826;;;_ = allout-encrypt-unencrypted-on-saves
96b83743 827(defcustom allout-encrypt-unencrypted-on-saves t
13aa217b
KM
828 "If non-nil, topics pending encryption are encrypted during buffer saves.
829
e1dbe924 830This prevents file-system exposure of un-encrypted contents of
13aa217b
KM
831items marked for encryption.
832
833When non-nil, if the topic currently being edited is decrypted,
834it will be encrypted for saving but automatically decrypted
835before any subsequent user interaction, so it is once again clear
836text for editing though the file system copy is encrypted.
837
838\(Auto-saves are handled differently. Buffers with plain-text
839exposed encrypted topics are exempted from auto saves until all
840such topics are encrypted.)"
841
842 :type 'boolean
843 :version "23.1"
8570ce80 844 :group 'allout-encryption)
d82979ea 845(make-variable-buffer-local 'allout-encrypt-unencrypted-on-saves)
13aa217b
KM
846(defvar allout-auto-save-temporarily-disabled nil
847 "True while topic encryption is pending and auto-saving was active.
848
20db1522 849The value of `buffer-saved-size' at the time of decryption is used,
13aa217b
KM
850for restoring when all encryptions are established.")
851(defvar allout-just-did-undo nil
852 "True just after undo commands, until allout-post-command-business.")
853(make-variable-buffer-local 'allout-just-did-undo)
d82979ea 854
01fc9422
EZ
855;;;_ + Developer
856;;;_ = allout-developer group
857(defgroup allout-developer nil
1c9b9df0 858 "Allout settings developers care about, including topic encryption and more."
01fc9422
EZ
859 :group 'allout)
860;;;_ = allout-run-unit-tests-on-load
861(defcustom allout-run-unit-tests-on-load nil
9201cc28 862 "When non-nil, unit tests will be run at end of loading the allout module.
01fc9422
EZ
863
864Generally, allout code developers are the only ones who'll want to set this.
865
866\(If set, this makes it an even better practice to exercise changes by
5204800f
NR
867doing byte-compilation with a repeat count, so the file is loaded after
868compilation.)
01fc9422
EZ
869
870See `allout-run-unit-tests' to see what's run."
871 :type 'boolean
872 :group 'allout-developer)
873
c567ac01
RS
874;;;_ + Miscellaneous customization
875
d82979ea
EZ
876;;;_ = allout-enable-file-variable-adjustment
877(defcustom allout-enable-file-variable-adjustment t
9201cc28 878 "If non-nil, some allout outline actions edit Emacs local file var text.
d82979ea
EZ
879
880This can range from changes to existing entries, addition of new ones,
881and creation of a new local variables section when necessary.
19b84ba3 882
d82979ea
EZ
883Emacs file variables adjustments are also inhibited if `enable-local-variables'
884is nil.
c567ac01 885
d82979ea 886Operations potentially causing edits include allout encryption routines.
69027dea 887For details, see `allout-toggle-current-subtree-encryption's docstring."
4bef9110
SE
888 :type 'boolean
889 :group 'allout)
d82979ea 890(make-variable-buffer-local 'allout-enable-file-variable-adjustment)
c567ac01 891
60c7bbd2 892;;;_* CODE -- no user customizations below.
c567ac01 893
9179616f
DL
894;;;_ #1 Internal Outline Formatting and Configuration
895;;;_ : Version
fd5359c6 896;;;_ = allout-version
0af8c88b 897(defvar allout-version "2.3"
c07583cd 898 "Version of currently loaded outline package. (allout.el)")
fd5359c6
MR
899;;;_ > allout-version
900(defun allout-version (&optional here)
19b84ba3
RS
901 "Return string describing the loaded outline version."
902 (interactive "P")
fd5359c6 903 (let ((msg (concat "Allout Outline Mode v " allout-version)))
eac9cf5f 904 (if here (insert msg))
19b84ba3
RS
905 (message "%s" msg)
906 msg))
69027dea
EZ
907;;;_ : Mode activation (defined here because it's referenced early)
908;;;_ = allout-mode
909(defvar allout-mode nil "Allout outline mode minor-mode flag.")
910(make-variable-buffer-local 'allout-mode)
4034b0e2 911;;;_ = allout-layout nil
60c7bbd2 912(defvar allout-layout nil ; LEAVE GLOBAL VALUE NIL -- see docstring.
4034b0e2
LT
913 "Buffer-specific setting for allout layout.
914
20db1522 915In buffers where this is non-nil (and if `allout-auto-activation'
11c46e15
KM
916has been customized to enable this behavior), `allout-mode' will be
917automatically activated. The layout dictated by the value will be used to
918set the initial exposure when `allout-mode' is activated.
4034b0e2
LT
919
920\*You should not setq-default this variable non-nil unless you want every
921visited file to be treated as an allout file.*
922
923The value would typically be set by a file local variable. For
924example, the following lines at the bottom of an Emacs Lisp file:
925
926;;;Local variables:
c07583cd 927;;;allout-layout: (0 : -1 -1 0)
4034b0e2
LT
928;;;End:
929
930dictate activation of `allout-mode' mode when the file is visited
11c46e15
KM
931\(presuming proper `allout-auto-activation' customization),
932followed by the equivalent of `(allout-expose-topic 0 : -1 -1 0)'.
933\(This is the layout used for the allout.el source file.)
4034b0e2
LT
934
935`allout-default-layout' describes the specification format.
936`allout-layout' can additionally have the value `t', in which
937case the value of `allout-default-layout' is used.")
938(make-variable-buffer-local 'allout-layout)
b1a569b5 939;;;###autoload
8570ce80 940(put 'allout-layout 'safe-local-variable
4f91a816 941 (lambda (x) (or (numberp x) (listp x) (memq x '(: * + -)))))
4034b0e2 942
9179616f 943;;;_ : Topic header format
fd5359c6
MR
944;;;_ = allout-regexp
945(defvar allout-regexp ""
fb7ada5f 946 "Regular expression to match the beginning of a heading line.
19b84ba3 947
c567ac01
RS
948Any line whose beginning matches this regexp is considered a
949heading. This var is set according to the user configuration vars
2265e017 950by `set-allout-regexp'.")
fd5359c6
MR
951(make-variable-buffer-local 'allout-regexp)
952;;;_ = allout-bullets-string
953(defvar allout-bullets-string ""
19b84ba3
RS
954 "A string dictating the valid set of outline topic bullets.
955
60c7bbd2 956This var should *not* be set by the user -- it is set by `set-allout-regexp',
fd5359c6
MR
957and is produced from the elements of `allout-plain-bullets-string'
958and `allout-distinctive-bullets-string'.")
959(make-variable-buffer-local 'allout-bullets-string)
960;;;_ = allout-bullets-string-len
961(defvar allout-bullets-string-len 0
2265e017 962 "Length of current buffers' `allout-plain-bullets-string'.")
fd5359c6 963(make-variable-buffer-local 'allout-bullets-string-len)
d8350998
CY
964;;;_ = allout-depth-specific-regexp
965(defvar allout-depth-specific-regexp ""
fb7ada5f 966 "Regular expression to match a heading line prefix for a particular depth.
d8350998
CY
967
968This expression is used to search for depth-specific topic
969headers at depth 2 and greater. Use `allout-depth-one-regexp'
970for to seek topics at depth one.
971
972This var is set according to the user configuration vars by
973`set-allout-regexp'. It is prepared with format strings for two
974decimal numbers, which should each be one less than the depth of the
975topic prefix to be matched.")
976(make-variable-buffer-local 'allout-depth-specific-regexp)
977;;;_ = allout-depth-one-regexp
978(defvar allout-depth-one-regexp ""
fb7ada5f 979 "Regular expression to match a heading line prefix for depth one.
d8350998
CY
980
981This var is set according to the user configuration vars by
982`set-allout-regexp'. It is prepared with format strings for two
983decimal numbers, which should each be one less than the depth of the
984topic prefix to be matched.")
985(make-variable-buffer-local 'allout-depth-one-regexp)
fd5359c6
MR
986;;;_ = allout-line-boundary-regexp
987(defvar allout-line-boundary-regexp ()
42c3e51c 988 "`allout-regexp' prepended with a newline for the search target.
19b84ba3 989
d8350998 990This is properly set by `set-allout-regexp'.")
fd5359c6
MR
991(make-variable-buffer-local 'allout-line-boundary-regexp)
992;;;_ = allout-bob-regexp
993(defvar allout-bob-regexp ()
d8350998 994 "Like `allout-line-boundary-regexp', for headers at beginning of buffer.")
fd5359c6
MR
995(make-variable-buffer-local 'allout-bob-regexp)
996;;;_ = allout-header-subtraction
997(defvar allout-header-subtraction (1- (length allout-header-prefix))
998 "Allout-header prefix length to subtract when computing topic depth.")
999(make-variable-buffer-local 'allout-header-subtraction)
1000;;;_ = allout-plain-bullets-string-len
1001(defvar allout-plain-bullets-string-len (length allout-plain-bullets-string)
2265e017 1002 "Length of `allout-plain-bullets-string', updated by `set-allout-regexp'.")
fd5359c6
MR
1003(make-variable-buffer-local 'allout-plain-bullets-string-len)
1004
d8350998 1005;;;_ = allout-doublecheck-at-and-shallower
2a1408fd 1006(defconst allout-doublecheck-at-and-shallower 3
ede4ac6a 1007 "Validate apparent topics of this depth and shallower as being non-aberrant.
fd5359c6 1008
2a1408fd
DN
1009Verified with `allout-aberrant-container-p'. The usefulness of
1010this check is limited to shallow depths, because the
1011determination of aberrance is according to the mistaken item
1012being followed by a legitimate item of excessively greater depth.
1013
1014The classic example of a mistaken item, for a standard allout
1015outline configuration, is a body line that begins with an '...'
1016ellipsis. This happens to contain a legitimate depth-2 header
1017prefix, constituted by two '..' dots at the beginning of the
1018line. The only thing that can distinguish it *in principle* from
1019a legitimate one is if the following real header is at a depth
1020that is discontinuous from the depth of 2 implied by the
1021ellipsis, ie depth 4 or more. As the depth being tested gets
1022greater, the likelihood of this kind of disqualification is
1023lower, and the usefulness of this test is lower.
1024
1025Extending the depth of the doublecheck increases the amount it is
1026applied, increasing the cost of the test - on casual estimation,
1027for outlines with many deep topics, geometrically (O(n)?).
1028Taken together with decreasing likelihood that the test will be
1029useful at greater depths, more modest doublecheck limits are more
1030suitably economical.")
fd5359c6
MR
1031;;;_ X allout-reset-header-lead (header-lead)
1032(defun allout-reset-header-lead (header-lead)
9201cc28 1033 "Reset the leading string used to identify topic headers."
1977b8f6 1034 (interactive "sNew lead string: ")
fd5359c6
MR
1035 (setq allout-header-prefix header-lead)
1036 (setq allout-header-subtraction (1- (length allout-header-prefix)))
1037 (set-allout-regexp))
1038;;;_ X allout-lead-with-comment-string (header-lead)
1039(defun allout-lead-with-comment-string (&optional header-lead)
9201cc28 1040 "Set the topic-header leading string to specified string.
19b84ba3 1041
20db1522 1042Useful for encapsulating outline structure in programming
19b84ba3 1043language comments. Returns the leading string."
1977b8f6
RS
1044
1045 (interactive "P")
1046 (if (not (stringp header-lead))
1047 (setq header-lead (read-string
1048 "String prefix for topic headers: ")))
fd5359c6
MR
1049 (setq allout-reindent-bodies nil)
1050 (allout-reset-header-lead header-lead)
1977b8f6 1051 header-lead)
48bd8440
EZ
1052;;;_ > allout-infer-header-lead-and-primary-bullet ()
1053(defun allout-infer-header-lead-and-primary-bullet ()
1054 "Determine appropriate `allout-header-prefix' and `allout-primary-bullet'.
19b84ba3
RS
1055
1056Works according to settings of:
1057
8d118843 1058 `comment-start'
fd5359c6
MR
1059 `allout-header-prefix' (default)
1060 `allout-use-mode-specific-leader'
1061and `allout-mode-leaders'.
19b84ba3 1062
c07583cd 1063Apply this via (re)activation of `allout-mode', rather than
8d118843 1064invoking it directly."
fd5359c6
MR
1065 (let* ((use-leader (and (boundp 'allout-use-mode-specific-leader)
1066 (if (or (stringp allout-use-mode-specific-leader)
1067 (memq allout-use-mode-specific-leader
1068 '(allout-mode-leaders
19b84ba3
RS
1069 comment-start
1070 t)))
fd5359c6 1071 allout-use-mode-specific-leader
60c7bbd2 1072 ;; Oops -- garbled value, equate with effect of t:
19b84ba3
RS
1073 t)))
1074 (leader
1075 (cond
1076 ((not use-leader) nil)
1077 ;; Use the explicitly designated leader:
1078 ((stringp use-leader) use-leader)
fd5359c6 1079 (t (or (and (memq use-leader '(t allout-mode-leaders))
19b84ba3 1080 ;; Get it from outline mode leaders?
fd5359c6
MR
1081 (cdr (assq major-mode allout-mode-leaders)))
1082 ;; ... didn't get from allout-mode-leaders...
19b84ba3
RS
1083 (and (memq use-leader '(t comment-start))
1084 comment-start
1085 ;; Use comment-start, maybe tripled, and with
353e2ef2 1086 ;; underscore:
19b84ba3
RS
1087 (concat
1088 (if (string= " "
1089 (substring comment-start
1090 (1- (length comment-start))))
1091 ;; Use comment-start, sans trailing space:
1092 (substring comment-start 0 -1)
1093 (concat comment-start comment-start comment-start))
1094 ;; ... and append underscore, whichever:
1095 "_")))))))
1096 (if (not leader)
1097 nil
48bd8440
EZ
1098 (setq allout-header-prefix leader)
1099 (if (not allout-old-style-prefixes)
ea18b2b1 1100 ;; setting allout-primary-bullet makes the top level topics use --
60c7bbd2 1101 ;; actually, be -- the special prefix:
48bd8440
EZ
1102 (setq allout-primary-bullet leader))
1103 allout-header-prefix)))
1104(defalias 'allout-infer-header-lead
1105 'allout-infer-header-lead-and-primary-bullet)
fd5359c6
MR
1106;;;_ > allout-infer-body-reindent ()
1107(defun allout-infer-body-reindent ()
1108 "Determine proper setting for `allout-reindent-bodies'.
8d118843 1109
c07583cd 1110Depends on default setting of `allout-reindent-bodies' (which see)
8d118843
RS
1111and presence of setting for `comment-start', to tell whether the
1112file is programming code."
fd5359c6 1113 (if (and allout-reindent-bodies
8d118843 1114 comment-start
fd5359c6
MR
1115 (not (eq 'force allout-reindent-bodies)))
1116 (setq allout-reindent-bodies nil)))
1117;;;_ > set-allout-regexp ()
1118(defun set-allout-regexp ()
19b84ba3
RS
1119 "Generate proper topic-header regexp form for outline functions.
1120
fd5359c6 1121Works with respect to `allout-plain-bullets-string' and
d8350998
CY
1122`allout-distinctive-bullets-string'.
1123
1124Also refresh various data structures that hinge on the regexp."
1977b8f6
RS
1125
1126 (interactive)
fd5359c6
MR
1127 ;; Derive allout-bullets-string from user configured components:
1128 (setq allout-bullets-string "")
1129 (let ((strings (list 'allout-plain-bullets-string
1130 'allout-distinctive-bullets-string
1131 'allout-primary-bullet))
1977b8f6
RS
1132 cur-string
1133 cur-len
c567ac01 1134 cur-char
96b83743 1135 index)
1977b8f6 1136 (while strings
96b83743 1137 (setq index 0)
1977b8f6
RS
1138 (setq cur-len (length (setq cur-string (symbol-value (car strings)))))
1139 (while (< index cur-len)
1140 (setq cur-char (aref cur-string index))
fd5359c6
MR
1141 (setq allout-bullets-string
1142 (concat allout-bullets-string
1977b8f6
RS
1143 (cond
1144 ; Single dash would denote a
1145 ; sequence, repeated denotes
1146 ; a dash:
1147 ((eq cur-char ?-) "--")
1148 ; literal close-square-bracket
1149 ; doesn't work right in the
1150 ; expr, exclude it:
1151 ((eq cur-char ?\]) "")
1152 (t (regexp-quote (char-to-string cur-char))))))
1153 (setq index (1+ index)))
1154 (setq strings (cdr strings)))
1155 )
fd5359c6
MR
1156 ;; Derive next for repeated use in allout-pending-bullet:
1157 (setq allout-plain-bullets-string-len (length allout-plain-bullets-string))
1158 (setq allout-header-subtraction (1- (length allout-header-prefix)))
d8350998 1159
42c3e51c 1160 (let (new-part old-part formfeed-part)
d8350998
CY
1161 (setq new-part (concat "\\("
1162 (regexp-quote allout-header-prefix)
1163 "[ \t]*"
1164 ;; already regexp-quoted in a custom way:
1165 "[" allout-bullets-string "]"
1166 "\\)")
1167 old-part (concat "\\("
1168 (regexp-quote allout-primary-bullet)
1169 "\\|"
1170 (regexp-quote allout-header-prefix)
1171 "\\)"
1172 "+"
1173 " ?[^" allout-primary-bullet "]")
42c3e51c
KM
1174 formfeed-part "\\(\^L\\)"
1175
d8350998
CY
1176 allout-regexp (concat new-part
1177 "\\|"
1178 old-part
42c3e51c
KM
1179 "\\|"
1180 formfeed-part)
d8350998
CY
1181
1182 allout-line-boundary-regexp (concat "\n" new-part
1183 "\\|"
42c3e51c
KM
1184 "\n" old-part
1185 "\\|"
1186 "\n" formfeed-part)
d8350998
CY
1187
1188 allout-bob-regexp (concat "\\`" new-part
1189 "\\|"
42c3e51c
KM
1190 "\\`" old-part
1191 "\\|"
1192 "\\`" formfeed-part
1193 ))
d8350998
CY
1194
1195 (setq allout-depth-specific-regexp
1196 (concat "\\(^\\|\\`\\)"
1197 "\\("
1198
1199 ;; new-style spacers-then-bullet string:
1200 "\\("
1201 (allout-format-quote (regexp-quote allout-header-prefix))
1202 " \\{%s\\}"
1203 "[" (allout-format-quote allout-bullets-string) "]"
1204 "\\)"
1205
1206 ;; old-style all-bullets string, if primary not multi-char:
1207 (if (< 0 allout-header-subtraction)
1208 ""
1209 (concat "\\|\\("
1210 (allout-format-quote
1211 (regexp-quote allout-primary-bullet))
1212 (allout-format-quote
1213 (regexp-quote allout-primary-bullet))
1214 (allout-format-quote
1215 (regexp-quote allout-primary-bullet))
1216 "\\{%s\\}"
1217 ;; disqualify greater depths:
1218 "[^"
1219 (allout-format-quote allout-primary-bullet)
1220 "]\\)"
1221 ))
1222 "\\)"
1223 ))
1224 (setq allout-depth-one-regexp
1225 (concat "\\(^\\|\\`\\)"
1226 "\\("
1227
1228 "\\("
1229 (regexp-quote allout-header-prefix)
1230 ;; disqualify any bullet char following any amount of
1231 ;; intervening whitespace:
1232 " *"
1233 (concat "[^ " allout-bullets-string "]")
1234 "\\)"
1235 (if (< 0 allout-header-subtraction)
1236 ;; Need not support anything like the old
1237 ;; bullet style if the prefix is multi-char.
1238 ""
1239 (concat "\\|"
1240 (regexp-quote allout-primary-bullet)
1241 ;; disqualify deeper primary-bullet sequences:
1242 "[^" allout-primary-bullet "]"))
1243 "\\)"
1244 ))))
9179616f 1245;;;_ : Menu bar
95c12694
RS
1246(defvar allout-mode-exposure-menu)
1247(defvar allout-mode-editing-menu)
1248(defvar allout-mode-navigation-menu)
1249(defvar allout-mode-misc-menu)
fd5359c6 1250(defun produce-allout-mode-menubar-entries ()
9179616f 1251 (require 'easymenu)
fd5359c6 1252 (easy-menu-define allout-mode-exposure-menu
d3e51865 1253 allout-mode-map-value
9179616f
DL
1254 "Allout outline exposure menu."
1255 '("Exposure"
fd5359c6
MR
1256 ["Show Entry" allout-show-current-entry t]
1257 ["Show Children" allout-show-children t]
1258 ["Show Subtree" allout-show-current-subtree t]
1259 ["Hide Subtree" allout-hide-current-subtree t]
1260 ["Hide Leaves" allout-hide-current-leaves t]
9179616f 1261 "----"
fd5359c6
MR
1262 ["Show All" allout-show-all t]))
1263 (easy-menu-define allout-mode-editing-menu
d3e51865 1264 allout-mode-map-value
9179616f
DL
1265 "Allout outline editing menu."
1266 '("Headings"
fd5359c6
MR
1267 ["Open Sibling" allout-open-sibtopic t]
1268 ["Open Subtopic" allout-open-subtopic t]
1269 ["Open Supertopic" allout-open-supertopic t]
9179616f 1270 "----"
fd5359c6
MR
1271 ["Shift Topic In" allout-shift-in t]
1272 ["Shift Topic Out" allout-shift-out t]
1273 ["Rebullet Topic" allout-rebullet-topic t]
1274 ["Rebullet Heading" allout-rebullet-current-heading t]
d82979ea
EZ
1275 ["Number Siblings" allout-number-siblings t]
1276 "----"
1277 ["Toggle Topic Encryption"
1278 allout-toggle-current-subtree-encryption
1279 (> (allout-current-depth) 1)]))
fd5359c6 1280 (easy-menu-define allout-mode-navigation-menu
d3e51865 1281 allout-mode-map-value
9179616f
DL
1282 "Allout outline navigation menu."
1283 '("Navigation"
fd5359c6 1284 ["Next Visible Heading" allout-next-visible-heading t]
9179616f 1285 ["Previous Visible Heading"
fd5359c6 1286 allout-previous-visible-heading t]
9179616f 1287 "----"
fd5359c6
MR
1288 ["Up Level" allout-up-current-level t]
1289 ["Forward Current Level" allout-forward-current-level t]
9179616f 1290 ["Backward Current Level"
fd5359c6 1291 allout-backward-current-level t]
9179616f
DL
1292 "----"
1293 ["Beginning of Entry"
fd5359c6 1294 allout-beginning-of-current-entry t]
d82979ea 1295 ["End of Entry" allout-end-of-entry t]
fd5359c6
MR
1296 ["End of Subtree" allout-end-of-current-subtree t]))
1297 (easy-menu-define allout-mode-misc-menu
d3e51865 1298 allout-mode-map-value
9179616f
DL
1299 "Allout outlines miscellaneous bindings."
1300 '("Misc"
fd5359c6 1301 ["Version" allout-version t]
9179616f 1302 "----"
fd5359c6 1303 ["Duplicate Exposed" allout-copy-exposed-to-buffer t]
9179616f 1304 ["Duplicate Exposed, numbered"
71296446 1305 allout-flatten-exposed-to-buffer t]
9179616f 1306 ["Duplicate Exposed, indented"
71296446 1307 allout-indented-exposed-to-buffer t]
9179616f 1308 "----"
fd5359c6
MR
1309 ["Set Header Lead" allout-reset-header-lead t]
1310 ["Set New Exposure" allout-expose-topic t])))
01fc9422 1311;;;_ : Allout Modal-Variables Utilities
fd5359c6
MR
1312;;;_ = allout-mode-prior-settings
1313(defvar allout-mode-prior-settings nil
01fc9422 1314 "Internal `allout-mode' use; settings to be resumed on mode deactivation.
1977b8f6 1315
01fc9422
EZ
1316See `allout-add-resumptions' and `allout-do-resumptions'.")
1317(make-variable-buffer-local 'allout-mode-prior-settings)
1318;;;_ > allout-add-resumptions (&rest pairs)
1319(defun allout-add-resumptions (&rest pairs)
48bd8440 1320 "Set name/value PAIRS.
01fc9422
EZ
1321
1322Old settings are preserved for later resumption using `allout-do-resumptions'.
1323
48bd8440
EZ
1324The new values are set as a buffer local. On resumption, the prior buffer
1325scope of the variable is restored along with its value. If it was a void
1326buffer-local value, then it is left as nil on resumption.
1327
01fc9422 1328The pairs are lists whose car is the name of the variable and car of the
48bd8440
EZ
1329cdr is the new value: '(some-var some-value)'. The pairs can actually be
1330triples, where the third element qualifies the disposition of the setting,
1331as described further below.
01fc9422 1332
48bd8440
EZ
1333If the optional third element is the symbol 'extend, then the new value
1334created by `cons'ing the second element of the pair onto the front of the
1335existing value.
01fc9422 1336
48bd8440
EZ
1337If the optional third element is the symbol 'append, then the new value is
1338extended from the existing one by `append'ing a list containing the second
1339element of the pair onto the end of the existing value.
01fc9422 1340
48bd8440 1341Extension, and resumptions in general, should not be used for hook
60c7bbd2 1342functions -- use the 'local mode of `add-hook' for that, instead.
01fc9422
EZ
1343
1344The settings are stored on `allout-mode-prior-settings'."
1345 (while pairs
1346 (let* ((pair (pop pairs))
1347 (name (car pair))
48bd8440
EZ
1348 (value (cadr pair))
1349 (qualifier (if (> (length pair) 2)
1350 (caddr pair)))
1351 prior-value)
01fc9422
EZ
1352 (if (not (symbolp name))
1353 (error "Pair's name, %S, must be a symbol, not %s"
1354 name (type-of name)))
d8350998 1355 (setq prior-value (condition-case nil
48bd8440
EZ
1356 (symbol-value name)
1357 (void-variable nil)))
01fc9422
EZ
1358 (when (not (assoc name allout-mode-prior-settings))
1359 ;; Not already added as a resumption, create the prior setting entry.
1c9b9df0 1360 (if (local-variable-p name (current-buffer))
60c7bbd2 1361 ;; is already local variable -- preserve the prior value:
48bd8440 1362 (push (list name prior-value) allout-mode-prior-settings)
01fc9422
EZ
1363 ;; wasn't local variable, indicate so for resumption by killing
1364 ;; local value, and make it local:
1365 (push (list name) allout-mode-prior-settings)
1366 (make-local-variable name)))
48bd8440
EZ
1367 (if qualifier
1368 (cond ((eq qualifier 'extend)
1369 (if (not (listp prior-value))
1370 (error "extension of non-list prior value attempted")
1371 (set name (cons value prior-value))))
1372 ((eq qualifier 'append)
1373 (if (not (listp prior-value))
1374 (error "appending of non-list prior value attempted")
1375 (set name (append prior-value (list value)))))
1376 (t (error "unrecognized setting qualifier `%s' encountered"
1377 qualifier)))
1378 (set name value)))))
01fc9422
EZ
1379;;;_ > allout-do-resumptions ()
1380(defun allout-do-resumptions ()
1381 "Resume all name/value settings registered by `allout-add-resumptions'.
1382
1383This is used when concluding allout-mode, to resume selected variables to
1384their settings before allout-mode was started."
1385
1386 (while allout-mode-prior-settings
1387 (let* ((pair (pop allout-mode-prior-settings))
1388 (name (car pair))
1389 (value-cell (cdr pair)))
1390 (if (not value-cell)
1391 ;; Prior value was global:
1392 (kill-local-variable name)
1393 ;; Prior value was explicit:
1394 (set name (car value-cell))))))
9179616f 1395;;;_ : Mode-specific incidentals
fd5359c6 1396;;;_ > allout-unprotected (expr)
d82979ea 1397(defmacro allout-unprotected (expr)
96b83743 1398 "Enable internal outline operations to alter invisible text."
1b4d97b2 1399 `(let ((inhibit-read-only (if (not buffer-read-only) t))
0458fa80 1400 (inhibit-field-text-motion t))
96b83743
EZ
1401 ,expr))
1402;;;_ = allout-mode-hook
1403(defvar allout-mode-hook nil
7b97c764 1404 "Hook run when allout mode starts.")
01fc9422 1405;;;_ = allout-mode-deactivate-hook
7927f48d 1406(define-obsolete-variable-alias 'allout-mode-deactivate-hook
c6eee9aa 1407 'allout-mode-off-hook "24.1")
e5bd0a28
SM
1408(defvar allout-mode-deactivate-hook nil
1409 "Hook run when allout mode ends.")
01fc9422
EZ
1410;;;_ = allout-exposure-category
1411(defvar allout-exposure-category nil
1412 "Symbol for use as allout invisible-text overlay category.")
48bd8440 1413
7b97c764 1414;;;_ = allout-exposure-change-functions
e5bd0a28 1415(define-obsolete-variable-alias 'allout-exposure-change-hook
2a1e2476 1416 'allout-exposure-change-functions "24.3")
7b97c764
CY
1417(defcustom allout-exposure-change-functions nil
1418 "Abnormal hook run after allout outline subtree exposure changes.
48bd8440
EZ
1419It is run at the conclusion of `allout-flag-region'.
1420
1421Functions on the hook must take three arguments:
1422
60c7bbd2
RS
1423 - FROM -- integer indicating the point at the start of the change.
1424 - TO -- integer indicating the point of the end of the change.
1425 - FLAG -- change mode: nil for exposure, otherwise concealment.
48bd8440 1426
7b97c764
CY
1427This hook might be invoked multiple times by a single command."
1428 :type 'hook
1429 :group 'allout
2a1e2476 1430 :version "24.3")
48bd8440 1431
7b97c764 1432;;;_ = allout-structure-added-functions
e5bd0a28 1433(define-obsolete-variable-alias 'allout-structure-added-hook
2a1e2476 1434 'allout-structure-added-functions "24.3")
7b97c764
CY
1435(defcustom allout-structure-added-functions nil
1436 "Abnormal hook run after adding items to an Allout outline.
48bd8440
EZ
1437Functions on the hook should take two arguments:
1438
60c7bbd2
RS
1439 - NEW-START -- integer indicating position of start of the first new item.
1440 - NEW-END -- integer indicating position of end of the last new item.
48bd8440 1441
7b97c764
CY
1442This hook might be invoked multiple times by a single command."
1443 :type 'hook
1444 :group 'allout
2a1e2476 1445 :version "24.3")
7b97c764 1446
7b97c764 1447;;;_ = allout-structure-deleted-functions
e5bd0a28 1448(define-obsolete-variable-alias 'allout-structure-deleted-hook
2a1e2476 1449 'allout-structure-deleted-functions "24.3")
7b97c764
CY
1450(defcustom allout-structure-deleted-functions nil
1451 "Abnormal hook run after deleting subtrees from an Allout outline.
48bd8440
EZ
1452Functions on the hook must take two arguments:
1453
60c7bbd2
RS
1454 - DEPTH -- integer indicating the depth of the subtree that was deleted.
1455 - REMOVED-FROM -- integer indicating the point where the subtree was removed.
da506f05 1456
20db1522 1457Some edits that remove or invalidate items may be missed by this hook:
48bd8440 1458specifically edits that native allout routines do not control.
96b83743 1459
7b97c764
CY
1460This hook might be invoked multiple times by a single command."
1461 :type 'hook
1462 :group 'allout
2a1e2476 1463 :version "24.3")
48bd8440 1464
7b97c764 1465;;;_ = allout-structure-shifted-functions
e5bd0a28 1466(define-obsolete-variable-alias 'allout-structure-shifted-hook
2a1e2476 1467 'allout-structure-shifted-functions "24.3")
7b97c764
CY
1468(defcustom allout-structure-shifted-functions nil
1469 "Abnormal hook run after shifting items in an Allout outline.
48bd8440
EZ
1470Functions on the hook should take two arguments:
1471
60c7bbd2
RS
1472 - DEPTH-CHANGE -- integer indicating depth increase, negative for decrease
1473 - START -- integer indicating the start point of the shifted parent item.
48bd8440 1474
60c7bbd2 1475Some edits that shift items can be missed by this hook: specifically edits
48bd8440
EZ
1476that native allout routines do not control.
1477
7b97c764
CY
1478This hook might be invoked multiple times by a single command."
1479 :type 'hook
1480 :group 'allout
2a1e2476 1481 :version "24.3")
7b97c764 1482
4ec28e14 1483;;;_ = allout-after-copy-or-kill-hook
7b97c764
CY
1484(defcustom allout-after-copy-or-kill-hook nil
1485 "Normal hook run after copying outline text.."
1486 :type 'hook
1487 :group 'allout
2a1e2476 1488 :version "24.3")
4ec28e14 1489
919d884a 1490;;;_ = allout-post-undo-hook
7b97c764
CY
1491(defcustom allout-post-undo-hook nil
1492 "Normal hook run after undo activity.
919d884a 1493The item that's current when the hook is run *may* be the one
7b97c764
CY
1494that was affected by the undo.."
1495 :type 'hook
1496 :group 'allout
2a1e2476 1497 :version "24.3")
919d884a 1498
96b83743
EZ
1499;;;_ = allout-outside-normal-auto-fill-function
1500(defvar allout-outside-normal-auto-fill-function nil
20db1522 1501 "Value of `normal-auto-fill-function' outside of allout mode.
96b83743 1502
20db1522
JB
1503Used by `allout-auto-fill' to do the mandated `normal-auto-fill-function'
1504wrapped within allout's automatic `fill-prefix' setting.")
96b83743 1505(make-variable-buffer-local 'allout-outside-normal-auto-fill-function)
2576c28f
KM
1506;;;_ = prevent redundant activation by desktop mode:
1507(add-to-list 'desktop-minor-mode-handlers '(allout-mode . nil))
0949617b
EZ
1508;;;_ = allout-passphrase-verifier-string
1509(defvar allout-passphrase-verifier-string nil
1510 "Setting used to test solicited encryption passphrases against the one
1511already associated with a file.
1512
1513It consists of an encrypted random string useful only to verify that a
1514passphrase entered by the user is effective for decryption. The passphrase
1515itself is \*not* recorded in the file anywhere, and the encrypted contents
1516are random binary characters to avoid exposing greater susceptibility to
1517search attacks.
d82979ea
EZ
1518
1519The verifier string is retained as an Emacs file variable, as well as in
a0a022d5 1520the Emacs buffer state, if file variable adjustments are enabled. See
d82979ea 1521`allout-enable-file-variable-adjustment' for details about that.")
0949617b 1522(make-variable-buffer-local 'allout-passphrase-verifier-string)
59f7af81
CY
1523(make-obsolete-variable 'allout-passphrase-verifier-string
1524 'allout-passphrase-verifier-string "23.3")
8570ce80 1525;;;###autoload
7e5d77dc 1526(put 'allout-passphrase-verifier-string 'safe-local-variable 'stringp)
0949617b
EZ
1527;;;_ = allout-passphrase-hint-string
1528(defvar allout-passphrase-hint-string ""
1529 "Variable used to retain reminder string for file's encryption passphrase.
d82979ea 1530
0949617b 1531See the description of `allout-passphrase-hint-handling' for details about how
d82979ea
EZ
1532the reminder is deployed.
1533
a0a022d5 1534The hint is retained as an Emacs file variable, as well as in the Emacs buffer
d82979ea
EZ
1535state, if file variable adjustments are enabled. See
1536`allout-enable-file-variable-adjustment' for details about that.")
0949617b
EZ
1537(make-variable-buffer-local 'allout-passphrase-hint-string)
1538(setq-default allout-passphrase-hint-string "")
59f7af81
CY
1539(make-obsolete-variable 'allout-passphrase-hint-string
1540 'allout-passphrase-hint-string "23.3")
8570ce80
SM
1541;;;###autoload
1542(put 'allout-passphrase-hint-string 'safe-local-variable 'stringp)
d82979ea
EZ
1543;;;_ = allout-after-save-decrypt
1544(defvar allout-after-save-decrypt nil
1545 "Internal variable, is nil or has the value of two points:
1546
1547 - the location of a topic to be decrypted after saving is done
1548 - where to situate the cursor after the decryption is performed
1549
1550This is used to decrypt the topic that was currently being edited, if it
1551was encrypted automatically as part of a file write or autosave.")
1552(make-variable-buffer-local 'allout-after-save-decrypt)
48bd8440
EZ
1553;;;_ = allout-encryption-plaintext-sanitization-regexps
1554(defvar allout-encryption-plaintext-sanitization-regexps nil
1555 "List of regexps whose matches are removed from plaintext before encryption.
1556
1557This is for the sake of removing artifacts, like escapes, that are added on
1558and not actually part of the original plaintext. The removal is done just
1559prior to encryption.
1560
1561Entries must be symbols that are bound to the desired values.
1562
1563Each value can be a regexp or a list with a regexp followed by a
1564substitution string. If it's just a regexp, all its matches are removed
1565before the text is encrypted. If it's a regexp and a substitution, the
c7015153 1566substitution is used against the regexp matches, a la `replace-match'.")
30ae0b2c 1567(make-variable-buffer-local 'allout-encryption-plaintext-sanitization-regexps)
48bd8440
EZ
1568;;;_ = allout-encryption-ciphertext-rejection-regexps
1569(defvar allout-encryption-ciphertext-rejection-regexps nil
1570 "Variable for regexps matching plaintext to remove before encryption.
1571
ff3e8c8e 1572This is used to detect strings in encryption results that would
4c36be58 1573register as allout mode structural elements, for example, as a
ff3e8c8e 1574topic prefix.
48bd8440
EZ
1575
1576Entries must be symbols that are bound to the desired regexp values.
1577
ff3e8c8e
KM
1578Encryptions that result in matches will be retried, up to
1579`allout-encryption-ciphertext-rejection-limit' times, after which
1580an error is raised.")
48bd8440
EZ
1581
1582(make-variable-buffer-local 'allout-encryption-ciphertext-rejection-regexps)
1583;;;_ = allout-encryption-ciphertext-rejection-ceiling
1584(defvar allout-encryption-ciphertext-rejection-ceiling 5
1585 "Limit on number of times encryption ciphertext is rejected.
1586
1587See `allout-encryption-ciphertext-rejection-regexps' for rejection reasons.")
1588(make-variable-buffer-local 'allout-encryption-ciphertext-rejection-ceiling)
69027dea
EZ
1589;;;_ > allout-mode-p ()
1590;; Must define this macro above any uses, or byte compilation will lack
60c7bbd2 1591;; proper def, if file isn't loaded -- eg, during emacs build!
96497653 1592;;;###autoload
69027dea
EZ
1593(defmacro allout-mode-p ()
1594 "Return t if `allout-mode' is active in current buffer."
1595 'allout-mode)
13aa217b
KM
1596;;;_ > allout-write-contents-hook-handler ()
1597(defun allout-write-contents-hook-handler ()
1598 "Implement `allout-encrypt-unencrypted-on-saves' for file writes
1599
1600Return nil if all goes smoothly, or else return an informative
1601message if an error is encountered. The message will serve as a
1602non-nil return on `write-contents-functions' to prevent saving of
1603the buffer while it has decrypted content.
1604
20db1522 1605This behavior depends on Emacs versions that implement the
13aa217b 1606`write-contents-functions' hook."
d82979ea 1607
0949617b
EZ
1608 (if (or (not (allout-mode-p))
1609 (not (boundp 'allout-encrypt-unencrypted-on-saves))
d82979ea
EZ
1610 (not allout-encrypt-unencrypted-on-saves))
1611 nil
13aa217b
KM
1612 (if (save-excursion (goto-char (point-min))
1613 (allout-next-topic-pending-encryption))
1614 (progn
1615 (message "auto-encrypting pending topics")
1616 (sit-for 0)
1617 (condition-case failure
1618 (progn
d82979ea 1619 (setq allout-after-save-decrypt
13aa217b
KM
1620 (allout-encrypt-decrypted))
1621 ;; aok - return nil:
1622 nil)
1623 (error
1624 ;; whoops - probably some still-decrypted items, return non-nil:
1625 (let ((text (format (concat "%s contents write inhibited due to"
1626 " encrypted topic encryption error:"
1627 " %s")
1628 (buffer-name (current-buffer))
1629 failure)))
1630 (message text)(sit-for 2)
1631 text)))))
1632 ))
d82979ea
EZ
1633;;;_ > allout-after-saves-handler ()
1634(defun allout-after-saves-handler ()
1635 "Decrypt topic encrypted for save, if it's currently being edited.
1636
1637Ie, if it was pending encryption and contained the point in its body before
1638the save.
1639
1640We use values stored in `allout-after-save-decrypt' to locate the topic
1641and the place for the cursor after the decryption is done."
1642 (if (not (and (allout-mode-p)
1643 (boundp 'allout-after-save-decrypt)
1644 allout-after-save-decrypt))
1645 t
1646 (goto-char (car allout-after-save-decrypt))
1647 (let ((was-modified (buffer-modified-p)))
69027dea 1648 (allout-toggle-subtree-encryption)
d82979ea
EZ
1649 (if (not was-modified)
1650 (set-buffer-modified-p nil)))
1651 (goto-char (cadr allout-after-save-decrypt))
1652 (setq allout-after-save-decrypt nil))
1653 )
1c9b9df0
KM
1654;;;_ > allout-called-interactively-p ()
1655(defmacro allout-called-interactively-p ()
20db1522 1656 "A version of `called-interactively-p' independent of Emacs version."
1c9b9df0 1657 ;; ... to ease maintenance of allout without betraying deprecation.
23ba2705
SM
1658 (if (ignore-errors (called-interactively-p 'interactive) t)
1659 '(called-interactively-p 'interactive)
1660 '(called-interactively-p)))
498a6493
CY
1661;;;_ = allout-inhibit-aberrance-doublecheck nil
1662;; In some exceptional moments, disparate topic depths need to be allowed
1663;; momentarily, eg when one topic is being yanked into another and they're
1664;; about to be reconciled. let-binding allout-inhibit-aberrance-doublecheck
1665;; prevents the aberrance doublecheck to allow, eg, the reconciliation
1666;; processing to happen in the presence of such discrepancies. It should
1667;; almost never be needed, however.
1668(defvar allout-inhibit-aberrance-doublecheck nil
1669 "Internal state, for momentarily inhibits aberrance doublecheck.
1670
1671This should only be momentarily let-bound non-nil, not set
1672non-nil in a lasting way.")
19b84ba3 1673
1c9b9df0 1674;;;_ #2 Mode environment and activation
fd5359c6
MR
1675;;;_ = allout-explicitly-deactivated
1676(defvar allout-explicitly-deactivated nil
d82979ea 1677 "If t, `allout-mode's last deactivation was deliberate.
2265e017 1678So `allout-post-command-business' should not reactivate it...")
fd5359c6 1679(make-variable-buffer-local 'allout-explicitly-deactivated)
11c46e15
KM
1680;;;_ > allout-init (mode)
1681(defun allout-init (mode)
1682 "DEPRECATED - configure allout activation by customizing
1683`allout-auto-activation'. This function remains around, limited
01c35094 1684from what it did before, for backwards compatibility.
11c46e15
KM
1685
1686MODE is the activation mode - see `allout-auto-activation' for
1687valid values."
59f7af81 1688 (declare (obsolete allout-auto-activation "23.3"))
11c46e15
KM
1689 (custom-set-variables (list 'allout-auto-activation (format "%s" mode)))
1690 (format "%s" mode))
59f7af81 1691
fd5359c6
MR
1692;;;_ > allout-setup-menubar ()
1693(defun allout-setup-menubar ()
aad94676 1694 "Populate the current buffer's menubar with `allout-mode' stuff."
fd5359c6
MR
1695 (let ((menus (list allout-mode-exposure-menu
1696 allout-mode-editing-menu
1697 allout-mode-navigation-menu
1698 allout-mode-misc-menu))
9179616f
DL
1699 cur)
1700 (while menus
1701 (setq cur (car menus)
1702 menus (cdr menus))
1703 (easy-menu-add cur))))
01fc9422
EZ
1704;;;_ > allout-overlay-preparations
1705(defun allout-overlay-preparations ()
1706 "Set the properties of the allout invisible-text overlay and others."
1707 (setplist 'allout-exposure-category nil)
1708 (put 'allout-exposure-category 'invisible 'allout)
1709 (put 'allout-exposure-category 'evaporate t)
1c9b9df0 1710 ;; ??? We use isearch-open-invisible *and* isearch-mode-end-hook. The
ea18b2b1 1711 ;; latter would be sufficient, but it seems that a separate behavior --
60c7bbd2
RS
1712 ;; the _transient_ opening of invisible text during isearch -- is keyed to
1713 ;; presence of the isearch-open-invisible property -- even though this
96b83743 1714 ;; property controls the isearch _arrival_ behavior. This is the case at
21e8bc99 1715 ;; least in emacs 21, 22.1, and xemacs 21.4.
01fc9422 1716 (put 'allout-exposure-category 'isearch-open-invisible
96b83743
EZ
1717 'allout-isearch-end-handler)
1718 (if (featurep 'xemacs)
01fc9422
EZ
1719 (put 'allout-exposure-category 'start-open t)
1720 (put 'allout-exposure-category 'insert-in-front-hooks
96b83743 1721 '(allout-overlay-insert-in-front-handler)))
01fc9422
EZ
1722 (put 'allout-exposure-category 'modification-hooks
1723 '(allout-overlay-interior-modification-handler)))
d3e51865 1724;;;_ > define-minor-mode allout-mode
19b84ba3 1725;;;_ : Defun:
0949617b 1726;;;###autoload
d3e51865 1727(define-minor-mode allout-mode
c567ac01 1728;;;_ . Doc string:
06e21633
CY
1729 "Toggle Allout outline mode.
1730With a prefix argument ARG, enable Allout outline mode if ARG is
1731positive, and disable it otherwise. If called from Lisp, enable
1732the mode if ARG is omitted or nil.
19b84ba3 1733
06e21633
CY
1734\\<allout-mode-map-value>
1735Allout outline mode is a minor mode that provides extensive
1736outline oriented formatting and manipulation. It enables
1737structural editing of outlines, as well as navigation and
1738exposure. It also is specifically aimed at accommodating
20db1522 1739syntax-sensitive text like programming languages. (For example,
06e21633
CY
1740see the allout code itself, which is organized as an allout
1741outline.)
19b84ba3 1742
c07583cd 1743In addition to typical outline navigation and exposure, allout includes:
19b84ba3 1744
c07583cd
KS
1745 - topic-oriented authoring, including keystroke-based topic creation,
1746 repositioning, promotion/demotion, cut, and paste
9179616f 1747 - incremental search with dynamic exposure and reconcealment of hidden text
c07583cd 1748 - adjustable format, so programming code can be developed in outline-structure
2576c28f 1749 - easy topic encryption and decryption, symmetric or key-pair
c07583cd
KS
1750 - \"Hot-spot\" operation, for single-keystroke maneuvering and exposure control
1751 - integral outline layout, for automatic initial exposure when visiting a file
1752 - independent extensibility, using comprehensive exposure and authoring hooks
9179616f
DL
1753
1754and many other features.
19b84ba3 1755
2576c28f
KM
1756Below is a description of the key bindings, and then description
1757of special `allout-mode' features and terminology. See also the
1758outline menubar additions for quick reference to many of the
11c46e15 1759features. Customize `allout-auto-activation' to prepare your
20db1522 1760Emacs session for automatic activation of `allout-mode'.
9179616f 1761
d3e51865
KM
1762The bindings are those listed in `allout-prefixed-keybindings'
1763and `allout-unprefixed-keybindings'. We recommend customizing
1764`allout-command-prefix' to use just `\\C-c' as the command
1765prefix, if the allout bindings don't conflict with any personal
1766bindings you have on \\C-c. In any case, outline structure
1767navigation and authoring is simplified by positioning the cursor
1768on an item's bullet character, the \"hot-spot\" -- then you can
1769invoke allout commands with just the un-prefixed,
1770un-control-shifted command letters. This is described further in
1771the HOT-SPOT Operation section.
c07583cd
KS
1772
1773 Exposure Control:
1774 ----------------
1775\\[allout-hide-current-subtree] `allout-hide-current-subtree'
1776\\[allout-show-children] `allout-show-children'
1777\\[allout-show-current-subtree] `allout-show-current-subtree'
1778\\[allout-show-current-entry] `allout-show-current-entry'
1779\\[allout-show-all] `allout-show-all'
1780
1781 Navigation:
1782 ----------
1783\\[allout-next-visible-heading] `allout-next-visible-heading'
1784\\[allout-previous-visible-heading] `allout-previous-visible-heading'
1785\\[allout-up-current-level] `allout-up-current-level'
1786\\[allout-forward-current-level] `allout-forward-current-level'
1787\\[allout-backward-current-level] `allout-backward-current-level'
1788\\[allout-end-of-entry] `allout-end-of-entry'
1789\\[allout-beginning-of-current-entry] `allout-beginning-of-current-entry' (alternately, goes to hot-spot)
60c7bbd2 1790\\[allout-beginning-of-line] `allout-beginning-of-line' -- like regular beginning-of-line, but
c07583cd
KS
1791 if immediately repeated cycles to the beginning of the current item
1792 and then to the hot-spot (if `allout-beginning-of-line-cycles' is set).
1793
1794
1795 Topic Header Production:
1796 -----------------------
1797\\[allout-open-sibtopic] `allout-open-sibtopic' Create a new sibling after current topic.
1798\\[allout-open-subtopic] `allout-open-subtopic' ... an offspring of current topic.
1799\\[allout-open-supertopic] `allout-open-supertopic' ... a sibling of the current topic's parent.
1800
1801 Topic Level and Prefix Adjustment:
1802 ---------------------------------
1803\\[allout-shift-in] `allout-shift-in' Shift current topic and all offspring deeper
1804\\[allout-shift-out] `allout-shift-out' ... less deep
1805\\[allout-rebullet-current-heading] `allout-rebullet-current-heading' Prompt for alternate bullet for
1806 current topic
1807\\[allout-rebullet-topic] `allout-rebullet-topic' Reconcile bullets of topic and
20db1522 1808 its offspring -- distinctive bullets are not changed, others
c07583cd 1809 are alternated according to nesting depth.
ea18b2b1 1810\\[allout-number-siblings] `allout-number-siblings' Number bullets of topic and siblings --
c07583cd
KS
1811 the offspring are not affected.
1812 With repeat count, revoke numbering.
1813
1814 Topic-oriented Killing and Yanking:
1815 ----------------------------------
1816\\[allout-kill-topic] `allout-kill-topic' Kill current topic, including offspring.
1817\\[allout-copy-topic-as-kill] `allout-copy-topic-as-kill' Copy current topic, including offspring.
20db1522 1818\\[allout-kill-line] `allout-kill-line' Kill line, attending to outline structure.
c07583cd
KS
1819\\[allout-copy-line-as-kill] `allout-copy-line-as-kill' Copy line but don't delete it.
1820\\[allout-yank] `allout-yank' Yank, adjusting depth of yanked topic to
1821 depth of heading if yanking into bare topic
1822 heading (ie, prefix sans text).
20db1522 1823\\[allout-yank-pop] `allout-yank-pop' Is to `allout-yank' as `yank-pop' is to `yank'.
c07583cd
KS
1824
1825 Topic-oriented Encryption:
1826 -------------------------
1827\\[allout-toggle-current-subtree-encryption] `allout-toggle-current-subtree-encryption'
1828 Encrypt/Decrypt topic content
1829
1830 Misc commands:
1831 -------------
1832M-x outlineify-sticky Activate outline mode for current buffer,
1833 and establish a default file-var setting
1834 for `allout-layout'.
1835\\[allout-mark-topic] `allout-mark-topic'
1836\\[allout-copy-exposed-to-buffer] `allout-copy-exposed-to-buffer'
1837 Duplicate outline, sans concealed text, to
1838 buffer with name derived from derived from that
60c7bbd2 1839 of current buffer -- \"*BUFFERNAME exposed*\".
c07583cd
KS
1840\\[allout-flatten-exposed-to-buffer] `allout-flatten-exposed-to-buffer'
1841 Like above 'copy-exposed', but convert topic
1842 prefixes to section.subsection... numeric
1843 format.
11c46e15
KM
1844\\[customize-variable] allout-auto-activation
1845 Prepare Emacs session for allout outline mode
c07583cd 1846 auto-activation.
1977b8f6 1847
96b83743 1848 Topic Encryption
d82979ea 1849
96b83743 1850Outline mode supports gpg encryption of topics, with support for
29fac3fe
KM
1851symmetric and key-pair modes, and auto-encryption of topics
1852pending encryption on save.
d82979ea 1853
c07583cd 1854Topics pending encryption are, by default, automatically
29fac3fe
KM
1855encrypted during file saves, including checkpoint saves, to avoid
1856exposing the plain text of encrypted topics in the file system.
1857If the content of the topic containing the cursor was encrypted
1858for a save, it is automatically decrypted for continued editing.
c07583cd 1859
2576c28f
KM
1860NOTE: A few GnuPG v2 versions improperly preserve incorrect
1861symmetric decryption keys, preventing entry of the correct key on
1862subsequent decryption attempts until the cache times-out. That
20db1522 1863can take several minutes. (Decryption of other entries is not
2576c28f
KM
1864affected.) Upgrade your EasyPG version, if you can, and you can
1865deliberately clear your gpg-agent's cache by sending it a '-HUP'
1866signal.
c07583cd
KS
1867
1868See `allout-toggle-current-subtree-encryption' function docstring
1869and `allout-encrypt-unencrypted-on-saves' customization variable
1870for details.
d82979ea 1871
c07583cd 1872 HOT-SPOT Operation
c567ac01 1873
19b84ba3
RS
1874Hot-spot operation provides a means for easy, single-keystroke outline
1875navigation and exposure control.
c567ac01 1876
19b84ba3
RS
1877When the text cursor is positioned directly on the bullet character of
1878a topic, regular characters (a to z) invoke the commands of the
fd5359c6 1879corresponding allout-mode keymap control chars. For example, \"f\"
96b83743 1880would invoke the command typically bound to \"C-c<space>C-f\"
fd5359c6 1881\(\\[allout-forward-current-level] `allout-forward-current-level').
19b84ba3 1882
96b83743
EZ
1883Thus, by positioning the cursor on a topic bullet, you can
1884execute the outline navigation and manipulation commands with a
c07583cd 1885single keystroke. Regular navigation keys (eg, \\[forward-char], \\[next-line]) don't get
96b83743 1886this special translation, so you can use them to get out of the
c07583cd
KS
1887hot-spot and back to normal editing operation.
1888
d41b91e6 1889In allout-mode, the normal beginning-of-line command (\\[allout-beginning-of-line]) is
c07583cd
KS
1890replaced with one that makes it easy to get to the hot-spot. If you
1891repeat it immediately it cycles (if `allout-beginning-of-line-cycles'
1892is set) to the beginning of the item and then, if you hit it again
1893immediately, to the hot-spot. Similarly, `allout-beginning-of-current-entry'
1894\(\\[allout-beginning-of-current-entry]) moves to the hot-spot when the cursor is already located
1895at the beginning of the current entry.
1896
1897 Extending Allout
c567ac01 1898
fa463103 1899Allout exposure and authoring activities all have associated
c07583cd
KS
1900hooks, by which independent code can cooperate with allout
1901without changes to the allout core. Here are key ones:
1977b8f6 1902
c07583cd 1903`allout-mode-hook'
20db1522 1904`allout-mode-deactivate-hook' (deprecated)
2576c28f 1905`allout-mode-off-hook'
7b97c764
CY
1906`allout-exposure-change-functions'
1907`allout-structure-added-functions'
1908`allout-structure-deleted-functions'
1909`allout-structure-shifted-functions'
4ec28e14 1910`allout-after-copy-or-kill-hook'
919d884a 1911`allout-post-undo-hook'
c07583cd
KS
1912
1913 Terminology
1977b8f6 1914
60c7bbd2 1915Topic hierarchy constituents -- TOPICS and SUBTOPICS:
c567ac01 1916
c07583cd
KS
1917ITEM: A unitary outline element, including the HEADER and ENTRY text.
1918TOPIC: An ITEM and any ITEMs contained within it, ie having greater DEPTH
1919 and with no intervening items of lower DEPTH than the container.
1920CURRENT ITEM:
1921 The visible ITEM most immediately containing the cursor.
1922DEPTH: The degree of nesting of an ITEM; it increases with containment.
1923 The DEPTH is determined by the HEADER PREFIX. The DEPTH is also
1924 called the:
1925LEVEL: The same as DEPTH.
c567ac01
RS
1926
1927ANCESTORS:
c07583cd
KS
1928 Those ITEMs whose TOPICs contain an ITEM.
1929PARENT: An ITEM's immediate ANCESTOR. It has a DEPTH one less than that
1930 of the ITEM.
c567ac01 1931OFFSPRING:
c07583cd 1932 The ITEMs contained within an ITEM's TOPIC.
19b84ba3 1933SUBTOPIC:
c07583cd
KS
1934 An OFFSPRING of its ANCESTOR TOPICs.
1935CHILD:
1936 An immediate SUBTOPIC of its PARENT.
c567ac01 1937SIBLINGS:
c07583cd 1938 TOPICs having the same PARENT and DEPTH.
353e2ef2 1939
c567ac01
RS
1940Topic text constituents:
1941
c07583cd
KS
1942HEADER: The first line of an ITEM, include the ITEM PREFIX and HEADER
1943 text.
1944ENTRY: The text content of an ITEM, before any OFFSPRING, but including
1945 the HEADER text and distinct from the ITEM PREFIX.
1946BODY: Same as ENTRY.
1947PREFIX: The leading text of an ITEM which distinguishes it from normal
1948 ENTRY text. Allout recognizes the outline structure according
1949 to the strict PREFIX format. It consists of a PREFIX-LEAD string,
1950 PREFIX-PADDING, and a BULLET. The BULLET might be followed by a
d82979ea 1951 number, indicating the ordinal number of the topic among its
c07583cd
KS
1952 siblings, or an asterisk indicating encryption, plus an optional
1953 space. After that is the ITEM HEADER text, which is not part of
1954 the PREFIX.
c567ac01 1955
c07583cd
KS
1956 The relative length of the PREFIX determines the nesting DEPTH
1957 of the ITEM.
c567ac01 1958PREFIX-LEAD:
c07583cd
KS
1959 The string at the beginning of a HEADER PREFIX, by default a `.'.
1960 It can be customized by changing the setting of
1961 `allout-header-prefix' and then reinitializing `allout-mode'.
1962
1963 When the PREFIX-LEAD is set to the comment-string of a
1964 programming language, outline structuring can be embedded in
1965 program code without interfering with processing of the text
20db1522 1966 (by Emacs or the language processor) as program code. This
c07583cd
KS
1967 setting happens automatically when allout mode is used in
1968 programming-mode buffers. See `allout-use-mode-specific-leader'
1969 docstring for more detail.
c567ac01 1970PREFIX-PADDING:
c07583cd
KS
1971 Spaces or asterisks which separate the PREFIX-LEAD and the
1972 bullet, determining the ITEM's DEPTH.
1973BULLET: A character at the end of the ITEM PREFIX, it must be one of
1974 the characters listed on `allout-plain-bullets-string' or
38b2ca53
NR
1975 `allout-distinctive-bullets-string'. When creating a TOPIC,
1976 plain BULLETs are by default used, according to the DEPTH of the
1977 TOPIC. Choice among the distinctive BULLETs is offered when you
20db1522 1978 provide a universal argument (\\[universal-argument]) to the
e4769531 1979 TOPIC creation command, or when explicitly rebulleting a TOPIC. The
38b2ca53
NR
1980 significance of the various distinctive bullets is purely by
1981 convention. See the documentation for the above bullet strings for
1982 more details.
c567ac01 1983EXPOSURE:
c07583cd
KS
1984 The state of a TOPIC which determines the on-screen visibility
1985 of its OFFSPRING and contained ENTRY text.
c567ac01 1986CONCEALED:
c07583cd
KS
1987 TOPICs and ENTRY text whose EXPOSURE is inhibited. Concealed
1988 text is represented by \"...\" ellipses.
c567ac01 1989
c07583cd
KS
1990 CONCEALED TOPICs are effectively collapsed within an ANCESTOR.
1991CLOSED: A TOPIC whose immediate OFFSPRING and body-text is CONCEALED.
1992OPEN: A TOPIC that is not CLOSED, though its OFFSPRING or BODY may be."
c567ac01 1993;;;_ . Code
d3e51865
KM
1994 :lighter " Allout"
1995 :keymap 'allout-mode-map
1977b8f6 1996
13aa217b 1997 (let ((use-layout (if (listp allout-layout)
2576c28f
KM
1998 allout-layout
1999 allout-default-layout)))
1977b8f6 2000
d3e51865 2001 (if (not (allout-mode-p))
2576c28f
KM
2002 (progn
2003 ;; Deactivation:
2004
2005 ; Activation not explicitly
2006 ; requested, and either in
2007 ; active state or *de*activation
2008 ; specifically requested:
2009 (allout-do-resumptions)
2010
2011 (remove-from-invisibility-spec '(allout . t))
2012 (remove-hook 'pre-command-hook 'allout-pre-command-business t)
2013 (remove-hook 'post-command-hook 'allout-post-command-business t)
2014 (remove-hook 'before-change-functions 'allout-before-change-handler t)
2015 (remove-hook 'isearch-mode-end-hook 'allout-isearch-end-handler t)
13aa217b
KM
2016 (remove-hook 'write-contents-functions
2017 'allout-write-contents-hook-handler t)
2576c28f
KM
2018
2019 (remove-overlays (point-min) (point-max)
d3e51865 2020 'category 'allout-exposure-category))
2576c28f 2021
d3e51865 2022 ;; Activating:
fd5359c6 2023 (if allout-old-style-prefixes
01fc9422 2024 ;; Inhibit all the fancy formatting:
48bd8440 2025 (allout-add-resumptions '(allout-primary-bullet "*")))
19b84ba3 2026
01fc9422 2027 (allout-overlay-preparations) ; Doesn't hurt to redo this.
96b83743 2028
d8350998 2029 (allout-infer-header-lead-and-primary-bullet)
fd5359c6 2030 (allout-infer-body-reindent)
19b84ba3 2031
fd5359c6 2032 (set-allout-regexp)
2576c28f
KM
2033 (allout-add-resumptions '(allout-encryption-ciphertext-rejection-regexps
2034 allout-line-boundary-regexp
2035 extend)
2036 '(allout-encryption-ciphertext-rejection-regexps
2037 allout-bob-regexp
2038 extend))
19b84ba3 2039
d3e51865 2040 (allout-compose-and-institute-keymap)
01fc9422 2041 (produce-allout-mode-menubar-entries)
01fc9422 2042
96b83743 2043 (add-to-invisibility-spec '(allout . t))
38b2ca53 2044
01fc9422
EZ
2045 (allout-add-resumptions '(line-move-ignore-invisible t))
2046 (add-hook 'pre-command-hook 'allout-pre-command-business nil t)
2047 (add-hook 'post-command-hook 'allout-post-command-business nil t)
2576c28f 2048 (add-hook 'before-change-functions 'allout-before-change-handler nil t)
01fc9422 2049 (add-hook 'isearch-mode-end-hook 'allout-isearch-end-handler nil t)
13aa217b 2050 (add-hook 'write-contents-functions 'allout-write-contents-hook-handler
01fc9422 2051 nil t)
01fc9422
EZ
2052
2053 ;; Stash auto-fill settings and adjust so custom allout auto-fill
2054 ;; func will be used if auto-fill is active or activated. (The
2055 ;; custom func respects topic headline, maintains hanging-indents,
2056 ;; etc.)
01fc9422
EZ
2057 (allout-add-resumptions (list 'allout-former-auto-filler
2058 auto-fill-function)
2059 ;; Register allout-auto-fill to be used if
2060 ;; filling is active:
2061 (list 'allout-outside-normal-auto-fill-function
2062 normal-auto-fill-function)
2063 '(normal-auto-fill-function allout-auto-fill)
2064 ;; Paragraphs are broken by topic headlines.
2065 (list 'paragraph-start
2066 (concat paragraph-start "\\|^\\("
2067 allout-regexp "\\)"))
2068 (list 'paragraph-separate
2069 (concat paragraph-separate "\\|^\\("
2070 allout-regexp "\\)")))
2576c28f
KM
2071 (if (and auto-fill-function (not allout-inhibit-auto-fill))
2072 ;; allout-auto-fill will use the stashed values and so forth.
2073 (allout-add-resumptions '(auto-fill-function allout-auto-fill)))
2074
fd5359c6 2075 (allout-setup-menubar)
2576c28f
KM
2076
2077 ;; Do auto layout if warranted:
2078 (when (and allout-layout
2079 allout-auto-activation
2080 use-layout
11c46e15
KM
2081 (and (not (string= allout-auto-activation "activate"))
2082 (if (string= allout-auto-activation "ask")
2576c28f
KM
2083 (if (y-or-n-p (format "Expose %s with layout '%s'? "
2084 (buffer-name)
2085 use-layout))
2086 t
2087 (message "Skipped %s layout." (buffer-name))
2088 nil)
2089 t)))
2090 (save-excursion
2091 (message "Adjusting '%s' exposure..." (buffer-name))
2092 (goto-char 0)
2093 (allout-this-or-next-heading)
2094 (condition-case err
2095 (progn
2096 (apply 'allout-expose-topic (list use-layout))
2097 (message "Adjusting '%s' exposure... done."
2098 (buffer-name)))
2099 ;; Problem applying exposure -- notify user, but don't
2100 ;; interrupt, eg, file visit:
2101 (error (message "%s" (car (cdr err)))
2102 (sit-for 1))))
2103 ) ; when allout-layout
2104 ) ; if (allout-mode-p)
2105 ) ; let (())
2106 ) ; define-minor-mode
2107;;;_ > allout-minor-mode alias
2108(defalias 'allout-minor-mode 'allout-mode)
41ff6d0a
JB
2109;;;_ > allout-unload-function
2110(defun allout-unload-function ()
2111 "Unload the allout outline library."
2112 (save-current-buffer
2113 (dolist (buffer (buffer-list))
2114 (set-buffer buffer)
74f53697 2115 (when (allout-mode-p) (allout-mode -1))))
41ff6d0a
JB
2116 ;; continue standard unloading
2117 nil)
2118
773e7e48
EZ
2119;;;_ - Position Assessment
2120;;;_ > allout-hidden-p (&optional pos)
2121(defsubst allout-hidden-p (&optional pos)
1c9b9df0 2122 "Non-nil if the character after point was made invisible by allout."
da506f05 2123 (eq (get-char-property (or pos (point)) 'invisible) 'allout))
773e7e48 2124
96b83743
EZ
2125;;;_ > allout-overlay-insert-in-front-handler (ol after beg end
2126;;; &optional prelen)
9d3aa82c
JB
2127(defun allout-overlay-insert-in-front-handler (ol after beg _end
2128 &optional _prelen)
2a1408fd 2129 "Shift the overlay so stuff inserted in front of it is excluded."
96b83743 2130 (if after
1c9b9df0
KM
2131 ;; ??? Shouldn't moving the overlay should be unnecessary, if overlay
2132 ;; front-advance on the overlay worked as expected?
96b83743
EZ
2133 (move-overlay ol (1+ beg) (overlay-end ol))))
2134;;;_ > allout-overlay-interior-modification-handler (ol after beg end
2135;;; &optional prelen)
2136(defun allout-overlay-interior-modification-handler (ol after beg end
9d3aa82c 2137 &optional _prelen)
96b83743
EZ
2138 "Get confirmation before making arbitrary changes to invisible text.
2139
2140We expose the invisible text and ask for confirmation. Refusal or
75bcb912 2141`keyboard-quit' abandons the changes, with keyboard-quit additionally
96b83743
EZ
2142reclosing the opened text.
2143
60c7bbd2 2144No confirmation is necessary when `inhibit-read-only' is set -- eg, allout
96b83743
EZ
2145internal functions use this feature cohesively bunch changes."
2146
2147 (when (and (not inhibit-read-only) (not after))
2148 (let ((start (point))
2149 (ol-start (overlay-start ol))
2150 (ol-end (overlay-end ol))
96b83743
EZ
2151 first)
2152 (goto-char beg)
2153 (while (< (point) end)
2154 (when (allout-hidden-p)
2155 (allout-show-to-offshoot)
2156 (if (allout-hidden-p)
2157 (save-excursion (forward-char 1)
2158 (allout-show-to-offshoot)))
2159 (when (not first)
96b83743
EZ
2160 (setq first (point))))
2161 (goto-char (if (featurep 'xemacs)
2162 (next-property-change (1+ (point)) nil end)
2163 (next-char-property-change (1+ (point)) end))))
2164 (when first
2165 (goto-char first)
2166 (condition-case nil
2167 (if (not
2168 (yes-or-no-p
2169 (substitute-command-keys
4034b0e2 2170 (concat "Modify concealed text? (\"no\" just aborts,"
96b83743
EZ
2171 " \\[keyboard-quit] also reconceals) "))))
2172 (progn (goto-char start)
5a0c3f56 2173 (error "Concealed-text change refused")))
96b83743
EZ
2174 (quit (allout-flag-region ol-start ol-end nil)
2175 (allout-flag-region ol-start ol-end t)
5a0c3f56 2176 (error "Concealed-text change abandoned, text reconcealed"))))
96b83743
EZ
2177 (goto-char start))))
2178;;;_ > allout-before-change-handler (beg end)
2179(defun allout-before-change-handler (beg end)
2180 "Protect against changes to invisible text.
2181
60c7bbd2 2182See `allout-overlay-interior-modification-handler' for details."
d8350998 2183
13aa217b
KM
2184 (when (and (allout-mode-p) undo-in-progress)
2185 (setq allout-just-did-undo t)
2186 (if (allout-hidden-p)
2187 (allout-show-children)))
d8350998 2188
01fc9422
EZ
2189 ;; allout-overlay-interior-modification-handler on an overlay handles
2190 ;; this in other emacs, via `allout-exposure-category's 'modification-hooks.
2191 (when (and (featurep 'xemacs) (allout-mode-p))
2192 ;; process all of the pending overlays:
d8350998 2193 (save-excursion
f520c6f2 2194 (goto-char beg)
d8350998 2195 (let ((overlay (allout-get-invisibility-overlay)))
1c9b9df0
KM
2196 (if overlay
2197 (allout-overlay-interior-modification-handler
2198 overlay nil beg end nil))))))
96b83743 2199;;;_ > allout-isearch-end-handler (&optional overlay)
9d3aa82c 2200(defun allout-isearch-end-handler (&optional _overlay)
96b83743
EZ
2201 "Reconcile allout outline exposure on arriving in hidden text after isearch.
2202
2203Optional OVERLAY parameter is for when this function is used by
2204`isearch-open-invisible' overlay property. It is otherwise unused, so this
2205function can also be used as an `isearch-mode-end-hook'."
2206
2207 (if (and (allout-mode-p) (allout-hidden-p))
2208 (allout-show-to-offshoot)))
2209
60c7bbd2 2210;;;_ #3 Internal Position State-Tracking -- "allout-recent-*" funcs
a5a1e2ef
SM
2211;; All the basic outline functions that directly do string matches to
2212;; evaluate heading prefix location set the variables
2213;; `allout-recent-prefix-beginning' and `allout-recent-prefix-end'
2214;; when successful. Functions starting with `allout-recent-' all
2215;; use this state, providing the means to avoid redundant searches
2216;; for just-established data. This optimization can provide
2217;; significant speed improvement, but it must be employed carefully.
fd5359c6
MR
2218;;;_ = allout-recent-prefix-beginning
2219(defvar allout-recent-prefix-beginning 0
c567ac01 2220 "Buffer point of the start of the last topic prefix encountered.")
fd5359c6
MR
2221(make-variable-buffer-local 'allout-recent-prefix-beginning)
2222;;;_ = allout-recent-prefix-end
2223(defvar allout-recent-prefix-end 0
c567ac01 2224 "Buffer point of the end of the last topic prefix encountered.")
fd5359c6 2225(make-variable-buffer-local 'allout-recent-prefix-end)
d8350998
CY
2226;;;_ = allout-recent-depth
2227(defvar allout-recent-depth 0
2228 "Depth of the last topic prefix encountered.")
2229(make-variable-buffer-local 'allout-recent-depth)
fd5359c6
MR
2230;;;_ = allout-recent-end-of-subtree
2231(defvar allout-recent-end-of-subtree 0
2265e017 2232 "Buffer point last returned by `allout-end-of-current-subtree'.")
fd5359c6 2233(make-variable-buffer-local 'allout-recent-end-of-subtree)
d8350998
CY
2234;;;_ > allout-prefix-data ()
2235(defsubst allout-prefix-data ()
2236 "Register allout-prefix state data.
fd5359c6 2237
75bcb912
JB
2238For reference by `allout-recent' funcs. Return
2239the new value of `allout-recent-prefix-beginning'."
42c3e51c 2240 (setq allout-recent-prefix-end (or (match-end 1) (match-end 2) (match-end 3))
d8350998 2241 allout-recent-prefix-beginning (or (match-beginning 1)
42c3e51c
KM
2242 (match-beginning 2)
2243 (match-beginning 3))
d8350998
CY
2244 allout-recent-depth (max 1 (- allout-recent-prefix-end
2245 allout-recent-prefix-beginning
2246 allout-header-subtraction)))
2247 allout-recent-prefix-beginning)
2248;;;_ > nullify-allout-prefix-data ()
2249(defsubst nullify-allout-prefix-data ()
2250 "Mark allout prefix data as being uninformative."
2251 (setq allout-recent-prefix-end (point)
2252 allout-recent-prefix-beginning (point)
2253 allout-recent-depth 0)
2254 allout-recent-prefix-beginning)
fd5359c6 2255;;;_ > allout-recent-depth ()
d8350998 2256(defsubst allout-recent-depth ()
19b84ba3 2257 "Return depth of last heading encountered by an outline maneuvering function.
c567ac01
RS
2258
2259All outline functions which directly do string matches to assess
2265e017
MR
2260headings set the variables `allout-recent-prefix-beginning' and
2261`allout-recent-prefix-end' if successful. This function uses those settings
c567ac01
RS
2262to return the current depth."
2263
d8350998 2264 allout-recent-depth)
fd5359c6 2265;;;_ > allout-recent-prefix ()
d8350998 2266(defsubst allout-recent-prefix ()
2265e017 2267 "Like `allout-recent-depth', but returns text of last encountered prefix.
c567ac01
RS
2268
2269All outline functions which directly do string matches to assess
2265e017
MR
2270headings set the variables `allout-recent-prefix-beginning' and
2271`allout-recent-prefix-end' if successful. This function uses those settings
d8350998
CY
2272to return the current prefix."
2273 (buffer-substring-no-properties allout-recent-prefix-beginning
2274 allout-recent-prefix-end))
fd5359c6
MR
2275;;;_ > allout-recent-bullet ()
2276(defmacro allout-recent-bullet ()
20db1522 2277 "Like `allout-recent-prefix', but returns bullet of last encountered prefix.
c567ac01
RS
2278
2279All outline functions which directly do string matches to assess
2265e017
MR
2280headings set the variables `allout-recent-prefix-beginning' and
2281`allout-recent-prefix-end' if successful. This function uses those settings
c567ac01 2282to return the current depth of the most recently matched topic."
d8350998
CY
2283 '(buffer-substring-no-properties (1- allout-recent-prefix-end)
2284 allout-recent-prefix-end))
c567ac01 2285
19b84ba3 2286;;;_ #4 Navigation
c567ac01 2287
46771bb3 2288;;;_ - Position Assessment
c567ac01 2289;;;_ : Location Predicates
ede4ac6a
KS
2290;;;_ > allout-do-doublecheck ()
2291(defsubst allout-do-doublecheck ()
2292 "True if current item conditions qualify for checking on topic aberrance."
2293 (and
60c7bbd2 2294 ;; presume integrity of outline and yanked content during yank -- necessary
ede4ac6a 2295 ;; to allow for level disparity of yank location and yanked text:
498a6493 2296 (not allout-inhibit-aberrance-doublecheck)
ede4ac6a
KS
2297 ;; allout-doublecheck-at-and-shallower is ceiling for doublecheck:
2298 (<= allout-recent-depth allout-doublecheck-at-and-shallower)))
2299;;;_ > allout-aberrant-container-p ()
d8350998
CY
2300(defun allout-aberrant-container-p ()
2301 "True if topic, or next sibling with children, contains them discontinuously.
2302
2303Discontinuous means an immediate offspring that is nested more
2304than one level deeper than the topic.
2305
2306If topic has no offspring, then the next sibling with offspring will
2307determine whether or not this one is determined to be aberrant.
2308
2309If true, then the allout-recent-* settings are calibrated on the
e1dbe924 2310offspring that qualifies it as aberrant, ie with depth that
d8350998
CY
2311exceeds the topic by more than one."
2312
2313 ;; This is most clearly understood when considering standard-prefix-leader
2314 ;; low-level topics, which can all too easily match text not intended as
2315 ;; headers. For example, any line with a leading '.' or '*' and lacking a
2316 ;; following bullet qualifies without this protection. (A sequence of
2317 ;; them can occur naturally, eg a typical textual bullet list.) We
2318 ;; disqualify such low-level sequences when they are followed by a
2319 ;; discontinuously contained child, inferring that the sequences are not
2320 ;; actually connected with their prospective context.
2321
2322 (let ((depth (allout-depth))
2323 (start-point (point))
2324 done aberrant)
2a1408fd
DN
2325 (save-match-data
2326 (save-excursion
2327 (while (and (not done)
2328 (re-search-forward allout-line-boundary-regexp nil 0))
2329 (allout-prefix-data)
2330 (goto-char allout-recent-prefix-beginning)
2331 (cond
8dd59f01 2332 ;; sibling -- continue:
2a1408fd 2333 ((eq allout-recent-depth depth))
8dd59f01 2334 ;; first offspring is excessive -- aberrant:
2a1408fd
DN
2335 ((> allout-recent-depth (1+ depth))
2336 (setq done t aberrant t))
8dd59f01 2337 ;; next non-sibling is lower-depth -- not aberrant:
2a1408fd 2338 (t (setq done t))))))
d8350998
CY
2339 (if aberrant
2340 aberrant
2341 (goto-char start-point)
2342 ;; recalibrate allout-recent-*
2343 (allout-depth)
2344 nil)))
ede4ac6a
KS
2345;;;_ > allout-on-current-heading-p ()
2346(defun allout-on-current-heading-p ()
2347 "Return non-nil if point is on current visible topics' header line.
2348
2349Actually, returns prefix beginning point."
2350 (save-excursion
2351 (allout-beginning-of-current-line)
2a1408fd
DN
2352 (save-match-data
2353 (and (looking-at allout-regexp)
2354 (allout-prefix-data)
2355 (or (not (allout-do-doublecheck))
2356 (not (allout-aberrant-container-p)))))))
ede4ac6a
KS
2357;;;_ > allout-on-heading-p ()
2358(defalias 'allout-on-heading-p 'allout-on-current-heading-p)
2359;;;_ > allout-e-o-prefix-p ()
2360(defun allout-e-o-prefix-p ()
2361 "True if point is located where current topic prefix ends, heading begins."
2a1408fd
DN
2362 (and (save-match-data
2363 (save-excursion (let ((inhibit-field-text-motion t))
2364 (beginning-of-line))
2365 (looking-at allout-regexp))
2366 (= (point) (save-excursion (allout-end-of-prefix)(point))))))
c567ac01 2367;;;_ : Location attributes
fd5359c6 2368;;;_ > allout-depth ()
96b83743
EZ
2369(defun allout-depth ()
2370 "Return depth of topic most immediately containing point.
2371
42c3e51c
KM
2372Does not do doublecheck for aberrant topic header.
2373
96b83743
EZ
2374Return zero if point is not within any topic.
2375
2376Like `allout-current-depth', but respects hidden as well as visible topics."
9179616f 2377 (save-excursion
96b83743
EZ
2378 (let ((start-point (point)))
2379 (if (and (allout-goto-prefix)
2380 (not (< start-point (point))))
d8350998 2381 allout-recent-depth
96b83743 2382 (progn
d8350998
CY
2383 ;; Oops, no prefix, nullify it:
2384 (nullify-allout-prefix-data)
96b83743
EZ
2385 ;; ... and return 0:
2386 0)))))
fd5359c6 2387;;;_ > allout-current-depth ()
96b83743
EZ
2388(defun allout-current-depth ()
2389 "Return depth of visible topic most immediately containing point.
2390
2391Return zero if point is not within any topic."
2392 (save-excursion
2393 (if (allout-back-to-current-heading)
2394 (max 1
2395 (- allout-recent-prefix-end
2396 allout-recent-prefix-beginning
2397 allout-header-subtraction))
2398 0)))
fd5359c6
MR
2399;;;_ > allout-get-current-prefix ()
2400(defun allout-get-current-prefix ()
c567ac01 2401 "Topic prefix of the current topic."
1977b8f6 2402 (save-excursion
fd5359c6
MR
2403 (if (allout-goto-prefix)
2404 (allout-recent-prefix))))
2405;;;_ > allout-get-bullet ()
2406(defun allout-get-bullet ()
c567ac01 2407 "Return bullet of containing topic (visible or not)."
1977b8f6 2408 (save-excursion
fd5359c6
MR
2409 (and (allout-goto-prefix)
2410 (allout-recent-bullet))))
2411;;;_ > allout-current-bullet ()
2412(defun allout-current-bullet ()
c567ac01 2413 "Return bullet of current (visible) topic heading, or none if none found."
96b83743 2414 (condition-case nil
1977b8f6 2415 (save-excursion
fd5359c6 2416 (allout-back-to-current-heading)
d8350998
CY
2417 (buffer-substring-no-properties (- allout-recent-prefix-end 1)
2418 allout-recent-prefix-end))
1977b8f6 2419 ;; Quick and dirty provision, ostensibly for missing bullet:
d8350998 2420 (args-out-of-range nil))
1977b8f6 2421 )
fd5359c6
MR
2422;;;_ > allout-get-prefix-bullet (prefix)
2423(defun allout-get-prefix-bullet (prefix)
c567ac01 2424 "Return the bullet of the header prefix string PREFIX."
1977b8f6
RS
2425 ;; Doesn't make sense if we're old-style prefixes, but this just
2426 ;; oughtn't be called then, so forget about it...
fd5359c6 2427 (if (string-match allout-regexp prefix)
d8350998 2428 (substring prefix (1- (match-end 2)) (match-end 2))))
fd5359c6
MR
2429;;;_ > allout-sibling-index (&optional depth)
2430(defun allout-sibling-index (&optional depth)
9179616f
DL
2431 "Item number of this prospective topic among its siblings.
2432
e126900f 2433If optional arg DEPTH is greater than current depth, then we're
9179616f
DL
2434opening a new level, and return 0.
2435
2436If less than this depth, ascend to that depth and count..."
2437
2438 (save-excursion
2439 (cond ((and depth (<= depth 0) 0))
65e7eb3d 2440 ((or (null depth) (= depth (allout-depth)))
9179616f 2441 (let ((index 1))
d8350998 2442 (while (allout-previous-sibling allout-recent-depth nil)
9179616f
DL
2443 (setq index (1+ index)))
2444 index))
d8350998 2445 ((< depth allout-recent-depth)
fd5359c6
MR
2446 (allout-ascend-to-depth depth)
2447 (allout-sibling-index))
9179616f 2448 (0))))
fd5359c6
MR
2449;;;_ > allout-topic-flat-index ()
2450(defun allout-topic-flat-index ()
9179616f
DL
2451 "Return a list indicating point's numeric section.subsect.subsubsect...
2452Outermost is first."
fd5359c6
MR
2453 (let* ((depth (allout-depth))
2454 (next-index (allout-sibling-index depth))
9179616f
DL
2455 (rev-sibls nil))
2456 (while (> next-index 0)
2457 (setq rev-sibls (cons next-index rev-sibls))
2458 (setq depth (1- depth))
fd5359c6 2459 (setq next-index (allout-sibling-index depth)))
9179616f
DL
2460 rev-sibls)
2461 )
1977b8f6 2462
96b83743
EZ
2463;;;_ - Navigation routines
2464;;;_ > allout-beginning-of-current-line ()
2465(defun allout-beginning-of-current-line ()
2466 "Like beginning of line, but to visible text."
2467
5204800f
NR
2468 ;; This combination of move-beginning-of-line and beginning-of-line is
2469 ;; deliberate, but the (beginning-of-line) may now be superfluous.
0458fa80
EZ
2470 (let ((inhibit-field-text-motion t))
2471 (move-beginning-of-line 1)
96b83743 2472 (beginning-of-line)
0458fa80
EZ
2473 (while (and (not (bobp)) (or (not (bolp)) (allout-hidden-p)))
2474 (beginning-of-line)
2475 (if (or (allout-hidden-p) (not (bolp)))
2476 (forward-char -1)))))
96b83743
EZ
2477;;;_ > allout-end-of-current-line ()
2478(defun allout-end-of-current-line ()
2479 "Move to the end of line, past concealed text if any."
1c9b9df0 2480 ;; This is for symmetry with `allout-beginning-of-current-line' --
96b83743
EZ
2481 ;; `move-end-of-line' doesn't suffer the same problem as
2482 ;; `move-beginning-of-line'.
0458fa80 2483 (let ((inhibit-field-text-motion t))
96b83743 2484 (end-of-line)
0458fa80
EZ
2485 (while (allout-hidden-p)
2486 (end-of-line)
2487 (if (allout-hidden-p) (forward-char 1)))))
48bd8440
EZ
2488;;;_ > allout-beginning-of-line ()
2489(defun allout-beginning-of-line ()
2490 "Beginning-of-line with `allout-beginning-of-line-cycles' behavior, if set."
2491
2492 (interactive)
2493
2494 (if (or (not allout-beginning-of-line-cycles)
2495 (not (equal last-command this-command)))
2a1408fd
DN
2496 (progn
2497 (if (and (not (bolp))
2498 (allout-hidden-p (1- (point))))
1c9b9df0 2499 (goto-char (allout-previous-single-char-property-change
2a1408fd
DN
2500 (1- (point)) 'invisible)))
2501 (move-beginning-of-line 1))
d8350998
CY
2502 (allout-depth)
2503 (let ((beginning-of-body
2504 (save-excursion
ede4ac6a 2505 (while (and (allout-do-doublecheck)
d8350998
CY
2506 (allout-aberrant-container-p)
2507 (allout-previous-visible-heading 1)))
2508 (allout-beginning-of-current-entry)
2509 (point))))
48bd8440 2510 (cond ((= (current-column) 0)
d8350998 2511 (goto-char beginning-of-body))
48bd8440
EZ
2512 ((< (point) beginning-of-body)
2513 (allout-beginning-of-current-line))
2514 ((= (point) beginning-of-body)
2515 (goto-char (allout-current-bullet-pos)))
2516 (t (allout-beginning-of-current-line)
2517 (if (< (point) beginning-of-body)
2518 ;; we were on the headline after its start:
d8350998 2519 (goto-char beginning-of-body)))))))
48bd8440
EZ
2520;;;_ > allout-end-of-line ()
2521(defun allout-end-of-line ()
2522 "End-of-line with `allout-end-of-line-cycles' behavior, if set."
2523
2524 (interactive)
2525
2526 (if (or (not allout-end-of-line-cycles)
2527 (not (equal last-command this-command)))
2528 (allout-end-of-current-line)
2529 (let ((end-of-entry (save-excursion
2530 (allout-end-of-entry)
2531 (point))))
2532 (cond ((not (eolp))
2533 (allout-end-of-current-line))
2534 ((or (allout-hidden-p) (save-excursion
2535 (forward-char -1)
2536 (allout-hidden-p)))
2537 (allout-back-to-current-heading)
2538 (allout-show-current-entry)
d8350998 2539 (allout-show-children)
48bd8440
EZ
2540 (allout-end-of-entry))
2541 ((>= (point) end-of-entry)
2542 (allout-back-to-current-heading)
2543 (allout-end-of-current-line))
2a1408fd 2544 (t
1c9b9df0 2545 (if (not (allout-mark-active-p))
2a1408fd
DN
2546 (push-mark))
2547 (allout-end-of-entry))))))
1c9b9df0
KM
2548;;;_ > allout-mark-active-p ()
2549(defun allout-mark-active-p ()
2550 "True if the mark is currently or always active."
2551 ;; `(cond (boundp...))' (or `(if ...)') invokes special byte-compiler
0d26e0b6 2552 ;; provisions, at least in GNU Emacs to prevent warnings about lack of,
1c9b9df0
KM
2553 ;; eg, region-active-p.
2554 (cond ((boundp 'mark-active)
2555 mark-active)
2556 ((fboundp 'region-active-p)
2557 (region-active-p))
2558 (t)))
fd5359c6
MR
2559;;;_ > allout-next-heading ()
2560(defsubst allout-next-heading ()
c07583cd 2561 "Move to the heading for the topic (possibly invisible) after this one.
c567ac01 2562
d8350998 2563Returns the location of the heading, or nil if none found.
c567ac01 2564
75bcb912 2565We skip anomalous low-level topics, a la `allout-aberrant-container-p'."
2a1408fd
DN
2566 (save-match-data
2567
2568 (if (looking-at allout-regexp)
2569 (forward-char 1))
2570
2571 (when (re-search-forward allout-line-boundary-regexp nil 0)
2572 (allout-prefix-data)
42c3e51c
KM
2573 (goto-char allout-recent-prefix-beginning)
2574 (while (not (bolp))
2575 (forward-char -1))
2a1408fd
DN
2576 (and (allout-do-doublecheck)
2577 ;; this will set allout-recent-* on the first non-aberrant topic,
2578 ;; whether it's the current one or one that disqualifies it:
2579 (allout-aberrant-container-p))
42c3e51c 2580 ;; this may or may not be the same as above depending on doublecheck:
2a1408fd 2581 (goto-char allout-recent-prefix-beginning))))
96b83743 2582;;;_ > allout-this-or-next-heading
fd5359c6 2583(defun allout-this-or-next-heading ()
8d118843 2584 "Position cursor on current or next heading."
fd5359c6
MR
2585 ;; A throwaway non-macro that is defined after allout-next-heading
2586 ;; and usable by allout-mode.
d8350998 2587 (if (not (allout-goto-prefix-doublechecked)) (allout-next-heading)))
fd5359c6 2588;;;_ > allout-previous-heading ()
8a559d4e 2589(defun allout-previous-heading ()
c07583cd 2590 "Move to the prior (possibly invisible) heading line.
c567ac01 2591
d8350998
CY
2592Return the location of the beginning of the heading, or nil if not found.
2593
75bcb912 2594We skip anomalous low-level topics, a la `allout-aberrant-container-p'."
d8350998
CY
2595
2596 (if (bobp)
2597 nil
d8350998 2598 (let ((start-point (point)))
ede4ac6a 2599 ;; allout-goto-prefix-doublechecked calls us, so we can't use it here.
d8350998 2600 (allout-goto-prefix)
2a1408fd
DN
2601 (save-match-data
2602 (when (or (re-search-backward allout-line-boundary-regexp nil 0)
2603 (looking-at allout-bob-regexp))
2604 (goto-char (allout-prefix-data))
2605 (if (and (allout-do-doublecheck)
2606 (allout-aberrant-container-p))
2607 (or (allout-previous-heading)
2608 (and (goto-char start-point)
2609 ;; recalibrate allout-recent-*:
2610 (allout-depth)
2611 nil))
2612 (point)))))))
96b83743
EZ
2613;;;_ > allout-get-invisibility-overlay ()
2614(defun allout-get-invisibility-overlay ()
2615 "Return the overlay at point that dictates allout invisibility."
2616 (let ((overlays (overlays-at (point)))
2617 got)
2618 (while (and overlays (not got))
2619 (if (equal (overlay-get (car overlays) 'invisible) 'allout)
d8350998
CY
2620 (setq got (car overlays))
2621 (pop overlays)))
96b83743
EZ
2622 got))
2623;;;_ > allout-back-to-visible-text ()
2624(defun allout-back-to-visible-text ()
2625 "Move to most recent prior character that is visible, and return point."
2626 (if (allout-hidden-p)
2627 (goto-char (overlay-start (allout-get-invisibility-overlay))))
2628 (point))
c567ac01
RS
2629
2630;;;_ - Subtree Charting
2631;;;_ " These routines either produce or assess charts, which are
2632;;; nested lists of the locations of topics within a subtree.
2633;;;
d8350998
CY
2634;;; Charts enable efficient subtree navigation by providing a reusable basis
2635;;; for elaborate, compound assessment and adjustment of a subtree.
c567ac01 2636
48bd8440
EZ
2637;;;_ > allout-chart-subtree (&optional levels visible orig-depth prev-depth)
2638(defun allout-chart-subtree (&optional levels visible orig-depth prev-depth)
19b84ba3
RS
2639 "Produce a location \"chart\" of subtopics of the containing topic.
2640
c07583cd 2641Optional argument LEVELS specifies a depth limit (relative to start
65e7eb3d 2642depth) for the chart. Null LEVELS means no limit.
48bd8440
EZ
2643
2644When optional argument VISIBLE is non-nil, the chart includes
2645only the visible subelements of the charted subjects.
2646
65e7eb3d 2647The remaining optional args are for internal use by the function.
d82979ea
EZ
2648
2649Point is left at the end of the subtree.
19b84ba3 2650
d82979ea 2651Charts are used to capture outline structure, so that outline-altering
20db1522 2652routines need to assess the structure only once, and then use the chart
19b84ba3
RS
2653for their elaborate manipulations.
2654
d8350998
CY
2655The chart entries for the topics are in reverse order, so the
2656last topic is listed first. The entry for each topic consists of
2657an integer indicating the point at the beginning of the topic
20db1522 2658prefix. Charts for offspring consist of a list containing,
d8350998
CY
2659recursively, the charts for the respective subtopics. The chart
2660for a topics' offspring precedes the entry for the topic itself.
19b84ba3 2661
d82979ea
EZ
2662The other function parameters are for internal recursion, and should
2663not be specified by external callers. ORIG-DEPTH is depth of topic at
2664starting point, and PREV-DEPTH is depth of prior topic."
19b84ba3 2665
353e2ef2 2666 (let ((original (not orig-depth)) ; `orig-depth' set only in recursion.
19b84ba3
RS
2667 chart curr-depth)
2668
2669 (if original ; Just starting?
2670 ; Register initial settings and
2671 ; position to first offspring:
fd5359c6 2672 (progn (setq orig-depth (allout-depth))
19b84ba3 2673 (or prev-depth (setq prev-depth (1+ orig-depth)))
48bd8440
EZ
2674 (if visible
2675 (allout-next-visible-heading 1)
2676 (allout-next-heading))))
c567ac01 2677
19b84ba3
RS
2678 ;; Loop over the current levels' siblings. Besides being more
2679 ;; efficient than tail-recursing over a level, it avoids exceeding
e126900f 2680 ;; the typically quite constrained Emacs max-lisp-eval-depth.
9179616f 2681 ;;
19b84ba3
RS
2682 ;; Probably would speed things up to implement loop-based stack
2683 ;; operation rather than recursing for lower levels. Bah.
9179616f 2684
c567ac01 2685 (while (and (not (eobp))
19b84ba3 2686 ; Still within original topic?
d8350998 2687 (< orig-depth (setq curr-depth allout-recent-depth))
19b84ba3
RS
2688 (cond ((= prev-depth curr-depth)
2689 ;; Register this one and move on:
d8350998 2690 (setq chart (cons allout-recent-prefix-beginning chart))
19b84ba3 2691 (if (and levels (<= levels 1))
60c7bbd2 2692 ;; At depth limit -- skip sublevels:
fd5359c6 2693 (or (allout-next-sibling curr-depth)
60c7bbd2 2694 ;; or no more siblings -- proceed to
19b84ba3 2695 ;; next heading at lesser depth:
e6a9aec7 2696 (while (and (<= curr-depth
d8350998 2697 allout-recent-depth)
48bd8440
EZ
2698 (if visible
2699 (allout-next-visible-heading 1)
2700 (allout-next-heading)))))
2701 (if visible
2702 (allout-next-visible-heading 1)
2703 (allout-next-heading))))
19b84ba3
RS
2704
2705 ((and (< prev-depth curr-depth)
2706 (or (not levels)
2707 (> levels 0)))
2708 ;; Recurse on deeper level of curr topic:
2709 (setq chart
fd5359c6 2710 (cons (allout-chart-subtree (and levels
19b84ba3 2711 (1- levels))
48bd8440
EZ
2712 visible
2713 orig-depth
2714 curr-depth)
19b84ba3
RS
2715 chart))
2716 ;; ... then continue with this one.
2717 )
2718
2719 ;; ... else nil if we've ascended back to prev-depth.
2720
2721 )))
2722
2723 (if original ; We're at the last sibling on
2724 ; the original level. Position
2725 ; to the end of it:
c567ac01 2726 (progn (and (not (eobp)) (forward-char -1))
96b83743
EZ
2727 (and (= (preceding-char) ?\n)
2728 (= (aref (buffer-substring (max 1 (- (point) 3))
2729 (point))
2730 1)
2731 ?\n)
19b84ba3 2732 (forward-char -1))
fd5359c6 2733 (setq allout-recent-end-of-subtree (point))))
353e2ef2 2734
c567ac01
RS
2735 chart ; (nreverse chart) not necessary,
2736 ; and maybe not preferable.
2737 ))
fd5359c6 2738;;;_ > allout-chart-siblings (&optional start end)
9d3aa82c 2739(defun allout-chart-siblings (&optional _start _end)
c567ac01 2740 "Produce a list of locations of this and succeeding sibling topics.
fd5359c6 2741Effectively a top-level chart of siblings. See `allout-chart-subtree'
c567ac01
RS
2742for an explanation of charts."
2743 (save-excursion
d8350998
CY
2744 (when (allout-goto-prefix-doublechecked)
2745 (let ((chart (list (point))))
2746 (while (allout-next-sibling)
2747 (setq chart (cons (point) chart)))
2748 (if chart (setq chart (nreverse chart)))))))
fd5359c6
MR
2749;;;_ > allout-chart-to-reveal (chart depth)
2750(defun allout-chart-to-reveal (chart depth)
c567ac01 2751
19b84ba3 2752 "Return a flat list of hidden points in subtree CHART, up to DEPTH.
c567ac01 2753
65e7eb3d
EZ
2754If DEPTH is nil, include hidden points at any depth.
2755
19b84ba3 2756Note that point can be left at any of the points on chart, or at the
c567ac01
RS
2757start point."
2758
2759 (let (result here)
65e7eb3d 2760 (while (and (or (null depth) (> depth 0))
c567ac01
RS
2761 chart)
2762 (setq here (car chart))
2763 (if (listp here)
65e7eb3d
EZ
2764 (let ((further (allout-chart-to-reveal here (if (null depth)
2765 depth
2766 (1- depth)))))
60c7bbd2 2767 ;; We're on the start of a subtree -- recurse with it, if there's
c567ac01
RS
2768 ;; more depth to go:
2769 (if further (setq result (append further result)))
2770 (setq chart (cdr chart)))
2771 (goto-char here)
96b83743 2772 (if (allout-hidden-p)
c567ac01
RS
2773 (setq result (cons here result)))
2774 (setq chart (cdr chart))))
2775 result))
fd5359c6 2776;;;_ X allout-chart-spec (chart spec &optional exposing)
e126900f 2777;; (defun allout-chart-spec (chart spec &optional exposing)
c07583cd 2778;; "Not yet (if ever) implemented.
e126900f
JB
2779
2780;; Produce exposure directives given topic/subtree CHART and an exposure SPEC.
2781
2782;; Exposure spec indicates the locations to be exposed and the prescribed
2783;; exposure status. Optional arg EXPOSING is an integer, with 0
2784;; indicating pending concealment, anything higher indicating depth to
2785;; which subtopic headers should be exposed, and negative numbers
2786;; indicating (negative of) the depth to which subtopic headers and
2787;; bodies should be exposed.
2788
2789;; The produced list can have two types of entries. Bare numbers
2790;; indicate points in the buffer where topic headers that should be
2791;; exposed reside.
2792
2793;; - bare negative numbers indicates that the topic starting at the
2794;; point which is the negative of the number should be opened,
2795;; including their entries.
2796;; - bare positive values indicate that this topic header should be
2797;; opened.
2798;; - Lists signify the beginning and end points of regions that should
c07583cd 2799;; be flagged, and the flag to employ. (For concealment: `(\?r)', and
e126900f
JB
2800;; exposure:"
2801;; (while spec
2802;; (cond ((listp spec)
2803;; )
2804;; )
2805;; (setq spec (cdr spec)))
2806;; )
c567ac01
RS
2807
2808;;;_ - Within Topic
fd5359c6
MR
2809;;;_ > allout-goto-prefix ()
2810(defun allout-goto-prefix ()
9179616f 2811 "Put point at beginning of immediately containing outline topic.
19b84ba3 2812
9179616f 2813Goes to most immediate subsequent topic if none immediately containing.
19b84ba3
RS
2814
2815Not sensitive to topic visibility.
c567ac01 2816
d27081f5 2817Returns the point at the beginning of the prefix, or nil if none."
c567ac01 2818
2a1408fd
DN
2819 (save-match-data
2820 (let (done)
2821 (while (and (not done)
2822 (search-backward "\n" nil 1))
2823 (forward-char 1)
2824 (if (looking-at allout-regexp)
2825 (setq done (allout-prefix-data))
2826 (forward-char -1)))
2827 (if (bobp)
2828 (cond ((looking-at allout-regexp)
2829 (allout-prefix-data))
2830 ((allout-next-heading))
2831 (done))
2832 done))))
d8350998
CY
2833;;;_ > allout-goto-prefix-doublechecked ()
2834(defun allout-goto-prefix-doublechecked ()
2835 "Put point at beginning of immediately containing outline topic.
2836
c07583cd 2837Like `allout-goto-prefix', but shallow topics (according to
65e7eb3d
EZ
2838`allout-doublecheck-at-and-shallower') are checked and
2839disqualified for child containment discontinuity, according to
2840`allout-aberrant-container-p'."
ede4ac6a
KS
2841 (if (allout-goto-prefix)
2842 (if (and (allout-do-doublecheck)
2843 (allout-aberrant-container-p))
2844 (allout-previous-heading)
2845 (point))))
d8350998 2846
fd5359c6
MR
2847;;;_ > allout-end-of-prefix ()
2848(defun allout-end-of-prefix (&optional ignore-decorations)
19b84ba3
RS
2849 "Position cursor at beginning of header text.
2850
2851If optional IGNORE-DECORATIONS is non-nil, put just after bullet,
2852otherwise skip white space between bullet and ensuing text."
c567ac01 2853
d8350998 2854 (if (not (allout-goto-prefix-doublechecked))
1977b8f6 2855 nil
d8350998 2856 (goto-char allout-recent-prefix-end)
2a1408fd
DN
2857 (save-match-data
2858 (if ignore-decorations
2859 t
2860 (while (looking-at "[0-9]") (forward-char 1))
2861 (if (and (not (eolp)) (looking-at "\\s-")) (forward-char 1))))
1977b8f6 2862 ;; Reestablish where we are:
fd5359c6
MR
2863 (allout-current-depth)))
2864;;;_ > allout-current-bullet-pos ()
2865(defun allout-current-bullet-pos ()
c07583cd 2866 "Return position of current (visible) topic's bullet."
c567ac01 2867
8a559d4e 2868 (if (not (allout-current-depth))
c567ac01 2869 nil
8a559d4e 2870 (1- allout-recent-prefix-end)))
1c9b9df0
KM
2871;;;_ > allout-back-to-current-heading (&optional interactive)
2872(defun allout-back-to-current-heading (&optional interactive)
80f66ef4 2873 "Move to heading line of current topic, or beginning if not in a topic.
96b83743 2874
80f66ef4
CY
2875If interactive, we position at the end of the prefix.
2876
2877Return value of resulting point, unless we started outside
2878of (before any) topics, in which case we return nil."
96b83743 2879
1c9b9df0
KM
2880 (interactive "p")
2881
96b83743 2882 (allout-beginning-of-current-line)
80f66ef4 2883 (let ((bol-point (point)))
1e004a83
KM
2884 (when (allout-goto-prefix-doublechecked)
2885 (if (<= (point) bol-point)
2886 (progn
2887 (setq bol-point (point))
2888 (allout-beginning-of-current-line)
2889 (if (not (= bol-point (point)))
2890 (if (looking-at allout-regexp)
2891 (allout-prefix-data)))
1c9b9df0 2892 (if interactive
ede4ac6a 2893 (allout-end-of-prefix)
1e004a83
KM
2894 (point)))
2895 (goto-char (point-min))
2896 nil))))
fd5359c6
MR
2897;;;_ > allout-back-to-heading ()
2898(defalias 'allout-back-to-heading 'allout-back-to-current-heading)
96b83743
EZ
2899;;;_ > allout-pre-next-prefix ()
2900(defun allout-pre-next-prefix ()
1977b8f6
RS
2901 "Skip forward to just before the next heading line.
2902
c567ac01 2903Returns that character position."
1977b8f6 2904
8a559d4e
EZ
2905 (if (allout-next-heading)
2906 (goto-char (1- allout-recent-prefix-beginning))))
96b83743
EZ
2907;;;_ > allout-end-of-subtree (&optional current include-trailing-blank)
2908(defun allout-end-of-subtree (&optional current include-trailing-blank)
d82979ea
EZ
2909 "Put point at the end of the last leaf in the containing topic.
2910
96b83743
EZ
2911Optional CURRENT means put point at the end of the containing
2912visible topic.
2913
2914Optional INCLUDE-TRAILING-BLANK means include a trailing blank line, if
2915any, as part of the subtree. Otherwise, that trailing blank will be
2916excluded as delimiting whitespace between topics.
d82979ea
EZ
2917
2918Returns the value of point."
2919 (interactive "P")
2920 (if current
2921 (allout-back-to-current-heading)
d8350998
CY
2922 (allout-goto-prefix-doublechecked))
2923 (let ((level allout-recent-depth))
fd5359c6 2924 (allout-next-heading)
1977b8f6 2925 (while (and (not (eobp))
d8350998 2926 (> allout-recent-depth level))
fd5359c6 2927 (allout-next-heading))
48bd8440
EZ
2928 (if (eobp)
2929 (allout-end-of-entry)
2930 (forward-char -1))
96b83743 2931 (if (and (not include-trailing-blank) (= ?\n (preceding-char)))
c567ac01 2932 (forward-char -1))
fd5359c6 2933 (setq allout-recent-end-of-subtree (point))))
96b83743
EZ
2934;;;_ > allout-end-of-current-subtree (&optional include-trailing-blank)
2935(defun allout-end-of-current-subtree (&optional include-trailing-blank)
2936
d82979ea
EZ
2937 "Put point at end of last leaf in currently visible containing topic.
2938
96b83743
EZ
2939Optional INCLUDE-TRAILING-BLANK means include a trailing blank line, if
2940any, as part of the subtree. Otherwise, that trailing blank will be
2941excluded as delimiting whitespace between topics.
2942
d82979ea
EZ
2943Returns the value of point."
2944 (interactive)
96b83743 2945 (allout-end-of-subtree t include-trailing-blank))
1c9b9df0
KM
2946;;;_ > allout-beginning-of-current-entry (&optional interactive)
2947(defun allout-beginning-of-current-entry (&optional interactive)
d82979ea 2948 "When not already there, position point at beginning of current topic header.
c567ac01
RS
2949
2950If already there, move cursor to bullet for hot-spot operation.
46771bb3 2951\(See `allout-mode' doc string for details of hot-spot operation.)"
1c9b9df0 2952 (interactive "p")
c567ac01 2953 (let ((start-point (point)))
46771bb3 2954 (move-beginning-of-line 1)
d8350998
CY
2955 (if (< 0 (allout-current-depth))
2956 (goto-char allout-recent-prefix-end)
2957 (goto-char (point-min)))
fd5359c6 2958 (allout-end-of-prefix)
1c9b9df0 2959 (if (and interactive
c567ac01 2960 (= (point) start-point))
fd5359c6 2961 (goto-char (allout-current-bullet-pos)))))
96b83743
EZ
2962;;;_ > allout-end-of-entry (&optional inclusive)
2963(defun allout-end-of-entry (&optional inclusive)
2964 "Position the point at the end of the current topics' entry.
2965
2966Optional INCLUSIVE means also include trailing empty line, if any. When
2967unset, whitespace between items separates them even when the items are
2968collapsed."
1977b8f6 2969 (interactive)
96b83743
EZ
2970 (allout-pre-next-prefix)
2971 (if (and (not inclusive) (not (bobp)) (= ?\n (preceding-char)))
2972 (forward-char -1))
2973 (point))
fd5359c6
MR
2974;;;_ > allout-end-of-current-heading ()
2975(defun allout-end-of-current-heading ()
9179616f 2976 (interactive)
fd5359c6 2977 (allout-beginning-of-current-entry)
96b83743 2978 (search-forward "\n" nil t)
d82979ea 2979 (forward-char -1))
fd5359c6 2980(defalias 'allout-end-of-heading 'allout-end-of-current-heading)
d82979ea
EZ
2981;;;_ > allout-get-body-text ()
2982(defun allout-get-body-text ()
2983 "Return the unmangled body text of the topic immediately containing point."
2984 (save-excursion
2985 (allout-end-of-prefix)
96b83743 2986 (if (not (search-forward "\n" nil t))
d82979ea
EZ
2987 nil
2988 (backward-char 1)
2989 (let ((pre-body (point)))
2990 (if (not pre-body)
2991 nil
96b83743 2992 (allout-end-of-entry t)
d82979ea
EZ
2993 (if (not (= pre-body (point)))
2994 (buffer-substring-no-properties (1+ pre-body) (point))))
2995 )
2996 )
2997 )
2998 )
1977b8f6 2999
c567ac01 3000;;;_ - Depth-wise
fd5359c6
MR
3001;;;_ > allout-ascend-to-depth (depth)
3002(defun allout-ascend-to-depth (depth)
c567ac01 3003 "Ascend to depth DEPTH, returning depth if successful, nil if not."
fd5359c6 3004 (if (and (> depth 0)(<= depth (allout-depth)))
d8350998
CY
3005 (let (last-ascended)
3006 (while (and (< depth allout-recent-depth)
3007 (setq last-ascended (allout-ascend))))
3008 (goto-char allout-recent-prefix-beginning)
1c9b9df0 3009 (if (allout-called-interactively-p) (allout-end-of-prefix))
d8350998 3010 (and last-ascended allout-recent-depth))))
1c9b9df0 3011;;;_ > allout-ascend (&optional dont-move-if-unsuccessful)
c07583cd
KS
3012(defun allout-ascend (&optional dont-move-if-unsuccessful)
3013 "Ascend one level, returning resulting depth if successful, nil if not.
3014
3015Point is left at the beginning of the level whether or not
3016successful, unless optional DONT-MOVE-IF-UNSUCCESSFUL is set, in
3017which case point is returned to its original starting location."
3018 (if dont-move-if-unsuccessful
3019 (setq dont-move-if-unsuccessful (point)))
d82979ea
EZ
3020 (prog1
3021 (if (allout-beginning-of-level)
498a6493
CY
3022 (let ((bolevel (point))
3023 (bolevel-depth allout-recent-depth))
3024 (allout-previous-heading)
c07583cd
KS
3025 (cond ((< allout-recent-depth bolevel-depth)
3026 allout-recent-depth)
3027 ((= allout-recent-depth bolevel-depth)
3028 (if dont-move-if-unsuccessful
3029 (goto-char dont-move-if-unsuccessful))
3030 (allout-depth)
3031 nil)
3032 (t
3033 ;; some topic after very first is lower depth than first:
3034 (goto-char bolevel)
3035 (allout-depth)
3036 nil))))
1c9b9df0 3037 (if (allout-called-interactively-p) (allout-end-of-prefix))))
fd5359c6
MR
3038;;;_ > allout-descend-to-depth (depth)
3039(defun allout-descend-to-depth (depth)
19b84ba3
RS
3040 "Descend to depth DEPTH within current topic.
3041
3042Returning depth if successful, nil if not."
1977b8f6 3043 (let ((start-point (point))
fd5359c6 3044 (start-depth (allout-depth)))
1977b8f6 3045 (while
fd5359c6 3046 (and (> (allout-depth) 0)
d8350998 3047 (not (= depth allout-recent-depth)) ; ... not there yet
fd5359c6 3048 (allout-next-heading) ; ... go further
d8350998 3049 (< start-depth allout-recent-depth))) ; ... still in topic
fd5359c6 3050 (if (and (> (allout-depth) 0)
d8350998 3051 (= allout-recent-depth depth))
1977b8f6
RS
3052 depth
3053 (goto-char start-point)
3054 nil))
3055 )
d8350998 3056;;;_ > allout-up-current-level (arg)
9d3aa82c 3057(defun allout-up-current-level (_arg)
d8350998 3058 "Move out ARG levels from current visible topic."
d82979ea 3059 (interactive "p")
8a559d4e
EZ
3060 (let ((start-point (point)))
3061 (allout-back-to-current-heading)
3062 (if (not (allout-ascend))
3063 (progn (goto-char start-point)
3064 (error "Can't ascend past outermost level"))
1c9b9df0 3065 (if (allout-called-interactively-p) (allout-end-of-prefix))
8a559d4e 3066 allout-recent-prefix-beginning)))
1977b8f6 3067
c567ac01 3068;;;_ - Linear
fd5359c6
MR
3069;;;_ > allout-next-sibling (&optional depth backward)
3070(defun allout-next-sibling (&optional depth backward)
2265e017 3071 "Like `allout-forward-current-level', but respects invisible topics.
1977b8f6 3072
c567ac01 3073Traverse at optional DEPTH, or current depth if none specified.
1977b8f6 3074
c567ac01 3075Go backward if optional arg BACKWARD is non-nil.
1977b8f6 3076
d8350998 3077Return the start point of the new topic if successful, nil otherwise."
1977b8f6 3078
d8350998 3079 (if (if backward (bobp) (eobp))
1977b8f6 3080 nil
d8350998 3081 (let ((target-depth (or depth (allout-depth)))
1977b8f6 3082 (start-point (point))
ede4ac6a 3083 (start-prefix-beginning allout-recent-prefix-beginning)
d8350998
CY
3084 (count 0)
3085 leaping
c567ac01 3086 last-depth)
d8350998
CY
3087 (while (and
3088 ;; done too few single steps to resort to the leap routine:
3089 (not leaping)
3090 ;; not at limit:
3091 (not (if backward (bobp) (eobp)))
3092 ;; still traversable:
3093 (if backward (allout-previous-heading) (allout-next-heading))
3094 ;; we're below the target depth
3095 (> (setq last-depth allout-recent-depth) target-depth))
3096 (setq count (1+ count))
3097 (if (> count 7) ; lists are commonly 7 +- 2, right?-)
3098 (setq leaping t)))
3099 (cond (leaping
3100 (or (allout-next-sibling-leap target-depth backward)
3101 (progn
3102 (goto-char start-point)
3103 (if depth (allout-depth) target-depth)
3104 nil)))
3105 ((and (not (eobp))
3106 (and (> (or last-depth (allout-depth)) 0)
ede4ac6a
KS
3107 (= allout-recent-depth target-depth))
3108 (not (= start-prefix-beginning
3109 allout-recent-prefix-beginning)))
d8350998
CY
3110 allout-recent-prefix-beginning)
3111 (t
3112 (goto-char start-point)
3113 (if depth (allout-depth) target-depth)
3114 nil)))))
3115;;;_ > allout-next-sibling-leap (&optional depth backward)
3116(defun allout-next-sibling-leap (&optional depth backward)
3117 "Like `allout-next-sibling', but by direct search for topic at depth.
3118
3119Traverse at optional DEPTH, or current depth if none specified.
3120
3121Go backward if optional arg BACKWARD is non-nil.
3122
3123Return the start point of the new topic if successful, nil otherwise.
3124
3125Costs more than regular `allout-next-sibling' for short traversals:
3126
ee7683eb 3127 - we have to check the prior (next, if traveling backwards)
d8350998
CY
3128 item to confirm connectivity with the prior topic, and
3129 - if confirmed, we have to reestablish the allout-recent-* settings with
3130 some extra navigation
3131 - if confirmation fails, we have to do more work to recover
3132
3133It is an increasingly big win when there are many intervening
3134offspring before the next sibling, however, so
3135`allout-next-sibling' resorts to this if it finds itself in that
3136situation."
3137
3138 (if (if backward (bobp) (eobp))
3139 nil
3140 (let* ((start-point (point))
3141 (target-depth (or depth (allout-depth)))
3142 (search-whitespace-regexp nil)
3143 (depth-biased (- target-depth 2))
3144 (expression (if (<= target-depth 1)
3145 allout-depth-one-regexp
3146 (format allout-depth-specific-regexp
3147 depth-biased depth-biased)))
3148 found
3149 done)
3150 (while (not done)
2a1408fd
DN
3151 (setq found (save-match-data
3152 (if backward
3153 (re-search-backward expression nil 'to-limit)
3154 (forward-char 1)
3155 (re-search-forward expression nil 'to-limit))))
d8350998
CY
3156 (if (and found (allout-aberrant-container-p))
3157 (setq found nil))
3158 (setq done (or found (if backward (bobp) (eobp)))))
3159 (if (not found)
3160 (progn (goto-char start-point)
3161 nil)
3162 ;; rationale: if any intervening items were at a lower depth, we
60c7bbd2 3163 ;; would now be on the first offspring at the target depth -- ie,
97610156 3164 ;; the preceding item (per the search direction) must be at a
d8350998
CY
3165 ;; lesser depth. that's all we need to check.
3166 (if backward (allout-next-heading) (allout-previous-heading))
3167 (if (< allout-recent-depth target-depth)
3168 ;; return to start and reestablish allout-recent-*:
3169 (progn
3170 (goto-char start-point)
3171 (allout-depth)
3172 nil)
3173 (goto-char found)
3174 ;; locate cursor and set allout-recent-*:
3175 (allout-goto-prefix))))))
fd5359c6
MR
3176;;;_ > allout-previous-sibling (&optional depth backward)
3177(defun allout-previous-sibling (&optional depth backward)
d82979ea 3178 "Like `allout-forward-current-level' backwards, respecting invisible topics.
1977b8f6 3179
c567ac01 3180Optional DEPTH specifies depth to traverse, default current depth.
1977b8f6 3181
c567ac01 3182Optional BACKWARD reverses direction.
1977b8f6 3183
c567ac01 3184Return depth if successful, nil otherwise."
fd5359c6 3185 (allout-next-sibling depth (not backward))
1977b8f6 3186 )
fd5359c6
MR
3187;;;_ > allout-snug-back ()
3188(defun allout-snug-back ()
539d7736 3189 "Position cursor at end of previous topic.
19b84ba3
RS
3190
3191Presumes point is at the start of a topic prefix."
c567ac01
RS
3192 (if (or (bobp) (eobp))
3193 nil
3194 (forward-char -1))
96b83743 3195 (if (or (bobp) (not (= ?\n (preceding-char))))
c567ac01 3196 nil
96b83743 3197 (forward-char -1))
c567ac01 3198 (point))
fd5359c6
MR
3199;;;_ > allout-beginning-of-level ()
3200(defun allout-beginning-of-level ()
c567ac01 3201 "Go back to the first sibling at this level, visible or not."
fd5359c6
MR
3202 (allout-end-of-level 'backward))
3203;;;_ > allout-end-of-level (&optional backward)
9d3aa82c 3204(defun allout-end-of-level (&optional _backward)
c567ac01 3205 "Go to the last sibling at this level, visible or not."
1977b8f6 3206
fd5359c6
MR
3207 (let ((depth (allout-depth)))
3208 (while (allout-previous-sibling depth nil))
d8350998 3209 (prog1 allout-recent-depth
1c9b9df0 3210 (if (allout-called-interactively-p) (allout-end-of-prefix)))))
fd5359c6
MR
3211;;;_ > allout-next-visible-heading (arg)
3212(defun allout-next-visible-heading (arg)
20db1522 3213 "Move to the next ARGth visible heading line, backward if ARG is negative.
c567ac01 3214
96b83743 3215Move to buffer limit in indicated direction if headings are exhausted."
1977b8f6 3216
1977b8f6 3217 (interactive "p")
0458fa80
EZ
3218 (let* ((inhibit-field-text-motion t)
3219 (backward (if (< arg 0) (setq arg (* -1 arg))))
c567ac01 3220 (step (if backward -1 1))
82b392a4 3221 (progress (allout-current-bullet-pos))
c567ac01
RS
3222 prev got)
3223
d8350998
CY
3224 (while (> arg 0)
3225 (while (and
3226 ;; Boundary condition:
3227 (not (if backward (bobp)(eobp)))
3228 ;; Move, skipping over all concealed lines in one fell swoop:
3229 (prog1 (condition-case nil (or (line-move step) t)
3230 (error nil))
82b392a4
KM
3231 (allout-beginning-of-current-line)
3232 ;; line-move can wind up on the same line if long.
3233 ;; when moving forward, that would yield no-progress
3234 (when (and (not backward)
3235 (<= (point) progress))
3236 ;; ensure progress by doing line-move from end-of-line:
3237 (end-of-line)
3238 (condition-case nil (or (line-move step) t)
3239 (error nil))
3240 (allout-beginning-of-current-line)
3241 (setq progress (point))))
d8350998 3242 ;; Deal with apparent header line:
2a1408fd
DN
3243 (save-match-data
3244 (if (not (looking-at allout-regexp))
3245 ;; not a header line, keep looking:
d8350998 3246 t
2a1408fd
DN
3247 (allout-prefix-data)
3248 (if (and (allout-do-doublecheck)
3249 (allout-aberrant-container-p))
3250 ;; skip this aberrant prospective header line:
3251 t
8dd59f01 3252 ;; this prospective headerline qualifies -- register:
2a1408fd
DN
3253 (setq got allout-recent-prefix-beginning)
3254 ;; and break the loop:
3255 nil)))))
c567ac01
RS
3256 ;; Register this got, it may be the last:
3257 (if got (setq prev got))
3258 (setq arg (1- arg)))
3259 (cond (got ; Last move was to a prefix:
d8350998 3260 (allout-end-of-prefix))
c567ac01 3261 (prev ; Last move wasn't, but prev was:
d8350998
CY
3262 (goto-char prev)
3263 (allout-end-of-prefix))
c567ac01 3264 ((not backward) (end-of-line) nil))))
fd5359c6
MR
3265;;;_ > allout-previous-visible-heading (arg)
3266(defun allout-previous-visible-heading (arg)
c567ac01 3267 "Move to the previous heading line.
1977b8f6 3268
c567ac01 3269With argument, repeats or can move forward if negative.
2265e017 3270A heading line is one that starts with a `*' (or that `allout-regexp'
c567ac01 3271matches)."
1977b8f6 3272 (interactive "p")
d8350998 3273 (prog1 (allout-next-visible-heading (- arg))
1c9b9df0 3274 (if (allout-called-interactively-p) (allout-end-of-prefix))))
fd5359c6 3275;;;_ > allout-forward-current-level (arg)
d82979ea 3276(defun allout-forward-current-level (arg)
19b84ba3 3277 "Position point at the next heading of the same level.
1977b8f6 3278
19b84ba3 3279Takes optional repeat-count, goes backward if count is negative.
1977b8f6 3280
19b84ba3 3281Returns resulting position, else nil if none found."
d82979ea 3282 (interactive "p")
fd5359c6 3283 (let ((start-depth (allout-current-depth))
19b84ba3 3284 (start-arg arg)
d8350998 3285 (backward (> 0 arg)))
19b84ba3
RS
3286 (if (= 0 start-depth)
3287 (error "No siblings, not in a topic..."))
3288 (if backward (setq arg (* -1 arg)))
d8350998
CY
3289 (allout-back-to-current-heading)
3290 (while (and (not (zerop arg))
3291 (if backward
3292 (allout-previous-sibling)
3293 (allout-next-sibling)))
3294 (setq arg (1- arg)))
1c9b9df0 3295 (if (not (allout-called-interactively-p))
d8350998
CY
3296 nil
3297 (allout-end-of-prefix)
3298 (if (not (zerop arg))
3299 (error "Hit %s level %d topic, traversed %d of %d requested"
3300 (if backward "first" "last")
3301 allout-recent-depth
3302 (- (abs start-arg) arg)
3303 (abs start-arg))))))
fd5359c6 3304;;;_ > allout-backward-current-level (arg)
d82979ea 3305(defun allout-backward-current-level (arg)
fd5359c6 3306 "Inverse of `allout-forward-current-level'."
d82979ea 3307 (interactive "p")
1c9b9df0 3308 (if (allout-called-interactively-p)
19b84ba3 3309 (let ((current-prefix-arg (* -1 arg)))
fd5359c6
MR
3310 (call-interactively 'allout-forward-current-level))
3311 (allout-forward-current-level (* -1 arg))))
c567ac01 3312
19b84ba3 3313;;;_ #5 Alteration
c567ac01
RS
3314
3315;;;_ - Fundamental
fd5359c6
MR
3316;;;_ = allout-post-goto-bullet
3317(defvar allout-post-goto-bullet nil
3318 "Outline internal var, for `allout-pre-command-business' hot-spot operation.
19b84ba3
RS
3319
3320When set, tells post-processing to reposition on topic bullet, and
2265e017 3321then unset it. Set by `allout-pre-command-business' when implementing
19b84ba3
RS
3322hot-spot operation, where literal characters typed over a topic bullet
3323are mapped to the command of the corresponding control-key on the
d3e51865 3324`allout-mode-map-value'.")
fd5359c6 3325(make-variable-buffer-local 'allout-post-goto-bullet)
48bd8440
EZ
3326;;;_ = allout-command-counter
3327(defvar allout-command-counter 0
3328 "Counter that monotonically increases in allout-mode buffers.
3329
3330Set by `allout-pre-command-business', to support allout addons in
3331coordinating with allout activity.")
3332(make-variable-buffer-local 'allout-command-counter)
3f2b07f8
KM
3333;;;_ = allout-this-command-hid-text
3334(defvar allout-this-command-hid-text nil
3335 "True if the most recent allout-mode command hid any text.")
3336(make-variable-buffer-local 'allout-this-command-hid-text)
fd5359c6
MR
3337;;;_ > allout-post-command-business ()
3338(defun allout-post-command-business ()
2265e017 3339 "Outline `post-command-hook' function.
c567ac01 3340
2265e017 3341- Implement (and clear) `allout-post-goto-bullet', for hot-spot
c567ac01
RS
3342 outline commands.
3343
3f2b07f8
KM
3344- Move the cursor to the beginning of the entry if it is hidden
3345 and collapsing activity just happened.
3346
13aa217b
KM
3347- If the command we're following was an undo, check for change in
3348 the status of encrypted items and adjust auto-save inhibitions
3349 accordingly.
3350
96b83743 3351- Decrypt topic currently being edited if it was encrypted for a save."
c567ac01 3352
fd5359c6 3353 (if (not (allout-mode-p)) ; In allout-mode.
19b84ba3 3354 nil
d82979ea 3355
13aa217b
KM
3356 (when allout-just-did-undo
3357 (setq allout-just-did-undo nil)
919d884a 3358 (run-hooks 'allout-post-undo-hook)
13aa217b
KM
3359 (cond ((and (= buffer-saved-size -1)
3360 allout-auto-save-temporarily-disabled)
99d99081 3361 ;; user possibly undid a decryption, disinhibit auto-save:
13aa217b
KM
3362 (allout-maybe-resume-auto-save-info-after-encryption))
3363 ((save-excursion
3364 (save-restriction
3365 (widen)
3366 (goto-char (point-min))
3367 (not (allout-next-topic-pending-encryption))))
3368 ;; plain-text encrypted items are present, inhibit auto-save:
3369 (allout-inhibit-auto-save-info-for-decryption (buffer-size)))))
3370
d82979ea
EZ
3371 (if (and (boundp 'allout-after-save-decrypt)
3372 allout-after-save-decrypt)
3373 (allout-after-saves-handler))
3374
48bd8440 3375 ;; Implement allout-post-goto-bullet, if set:
fd5359c6
MR
3376 (if (and allout-post-goto-bullet
3377 (allout-current-bullet-pos))
3378 (progn (goto-char (allout-current-bullet-pos))
3f2b07f8
KM
3379 (setq allout-post-goto-bullet nil))
3380 (when (and (allout-hidden-p) allout-this-command-hid-text)
3381 (allout-beginning-of-current-entry)))))
fd5359c6
MR
3382;;;_ > allout-pre-command-business ()
3383(defun allout-pre-command-business ()
2265e017 3384 "Outline `pre-command-hook' function for outline buffers.
48bd8440
EZ
3385
3386Among other things, implements special behavior when the cursor is on the
3387topic bullet character.
c567ac01 3388
d3e51865
KM
3389When the cursor is on the bullet character, self-insert
3390characters are reinterpreted as the corresponding
3391control-character in the `allout-mode-map-value'. The
3392`allout-mode' `post-command-hook' insures that the cursor which
3393has moved as a result of such reinterpretation is positioned on
3394the bullet character of the destination topic.
c567ac01 3395
c07583cd 3396The upshot is that you can get easy, single (ie, unmodified) key
9179616f 3397outline maneuvering operations by positioning the cursor on the bullet
58edceeb
JB
3398char. When in this mode you can use regular cursor-positioning
3399command/keystrokes to relocate the cursor off of a bullet character to
3400return to regular interpretation of self-insert characters."
d82979ea 3401
fd5359c6 3402 (if (not (allout-mode-p))
9179616f 3403 nil
48bd8440 3404 (setq allout-command-counter (1+ allout-command-counter))
3f2b07f8 3405 (setq allout-this-command-hid-text nil)
48bd8440 3406 ;; Do hot-spot navigation.
9179616f 3407 (if (and (eq this-command 'self-insert-command)
fd5359c6 3408 (eq (point)(allout-current-bullet-pos)))
01fc9422
EZ
3409 (allout-hotspot-key-handler))))
3410;;;_ > allout-hotspot-key-handler ()
3411(defun allout-hotspot-key-handler ()
3412 "Catchall handling of key bindings in hot-spots.
3413
3414Translates unmodified keystrokes to corresponding allout commands, when
20db1522
JB
3415they would qualify if prefixed with the `allout-command-prefix', and sets
3416`this-command' accordingly.
01fc9422
EZ
3417
3418Returns the qualifying command, if any, else nil."
3419 (interactive)
1c9b9df0 3420 (let* ((modified (event-modifiers last-command-event))
8989a920 3421 (key-num (cond ((numberp last-command-event) last-command-event)
01fc9422
EZ
3422 ;; for XEmacs character type:
3423 ((and (fboundp 'characterp)
8989a920
GM
3424 (apply 'characterp (list last-command-event)))
3425 (apply 'char-to-int (list last-command-event)))
01fc9422 3426 (t 0)))
d8350998 3427 mapped-binding)
01fc9422
EZ
3428
3429 (if (zerop key-num)
3430 nil
3431
d8350998
CY
3432 (if (and
3433 ;; exclude control chars and escape:
1c9b9df0 3434 (not modified)
d8350998
CY
3435 (<= 33 key-num)
3436 (setq mapped-binding
642f3c5c
KM
3437 (or
3438 ;; try control-modified versions of keys:
3439 (key-binding (vconcat allout-command-prefix
3440 (vector
3441 (if (and (<= 97 key-num) ; "a"
3442 (>= 122 key-num)) ; "z"
3443 (- key-num 96) key-num)))
3444 t)
3445 ;; try non-modified versions of keys:
3446 (key-binding (vconcat allout-command-prefix
3447 (vector key-num))
3448 t))))
60c7bbd2 3449 ;; Qualified as an allout command -- do hot-spot operation.
01fc9422 3450 (setq allout-post-goto-bullet t)
1c9b9df0
KM
3451 ;; accept-defaults nil, or else we get allout-item-icon-key-handler.
3452 (setq mapped-binding (key-binding (vector key-num))))
01fc9422
EZ
3453
3454 (while (keymapp mapped-binding)
3455 (setq mapped-binding
d8350998 3456 (lookup-key mapped-binding (vector (read-char)))))
01fc9422 3457
1c9b9df0
KM
3458 (when mapped-binding
3459 (setq this-command mapped-binding)))))
01fc9422 3460
fd5359c6
MR
3461;;;_ > allout-find-file-hook ()
3462(defun allout-find-file-hook ()
4034b0e2 3463 "Activate `allout-mode' on non-nil `allout-auto-activation', `allout-layout'.
fd5359c6 3464
11c46e15 3465See `allout-auto-activation' for setup instructions."
fd5359c6
MR
3466 (if (and allout-auto-activation
3467 (not (allout-mode-p))
3468 allout-layout)
2576c28f 3469 (allout-mode)))
c567ac01
RS
3470
3471;;;_ - Topic Format Assessment
fd5359c6
MR
3472;;;_ > allout-solicit-alternate-bullet (depth &optional current-bullet)
3473(defun allout-solicit-alternate-bullet (depth &optional current-bullet)
1977b8f6 3474
19b84ba3
RS
3475 "Prompt for and return a bullet char as an alternative to the current one.
3476
3477Offer one suitable for current depth DEPTH as default."
1977b8f6 3478
9179616f 3479 (let* ((default-bullet (or (and (stringp current-bullet) current-bullet)
fd5359c6
MR
3480 (allout-bullet-for-depth depth)))
3481 (sans-escapes (regexp-sans-escapes allout-bullets-string))
9179616f
DL
3482 choice)
3483 (save-excursion
fd5359c6 3484 (goto-char (allout-current-bullet-pos))
9179616f
DL
3485 (setq choice (solicit-char-in-string
3486 (format "Select bullet: %s ('%s' default): "
3487 sans-escapes
1c9b9df0 3488 (allout-substring-no-properties default-bullet))
9179616f
DL
3489 sans-escapes
3490 t)))
3491 (message "")
1977b8f6
RS
3492 (if (string= choice "") default-bullet choice))
3493 )
fd5359c6
MR
3494;;;_ > allout-distinctive-bullet (bullet)
3495(defun allout-distinctive-bullet (bullet)
e126900f 3496 "True if BULLET is one of those on `allout-distinctive-bullets-string'."
fd5359c6
MR
3497 (string-match (regexp-quote bullet) allout-distinctive-bullets-string))
3498;;;_ > allout-numbered-type-prefix (&optional prefix)
3499(defun allout-numbered-type-prefix (&optional prefix)
c567ac01 3500 "True if current header prefix bullet is numbered bullet."
fd5359c6
MR
3501 (and allout-numbered-bullet
3502 (string= allout-numbered-bullet
1977b8f6 3503 (if prefix
fd5359c6
MR
3504 (allout-get-prefix-bullet prefix)
3505 (allout-get-bullet)))))
d82979ea
EZ
3506;;;_ > allout-encrypted-type-prefix (&optional prefix)
3507(defun allout-encrypted-type-prefix (&optional prefix)
c07583cd 3508 "True if current header prefix bullet is for an encrypted entry (body)."
d82979ea
EZ
3509 (and allout-topic-encryption-bullet
3510 (string= allout-topic-encryption-bullet
3511 (if prefix
3512 (allout-get-prefix-bullet prefix)
3513 (allout-get-bullet)))))
fd5359c6
MR
3514;;;_ > allout-bullet-for-depth (&optional depth)
3515(defun allout-bullet-for-depth (&optional depth)
19b84ba3 3516 "Return outline topic bullet suited to optional DEPTH, or current depth."
1977b8f6 3517 ;; Find bullet in plain-bullets-string modulo DEPTH.
fd5359c6
MR
3518 (if allout-stylish-prefixes
3519 (char-to-string (aref allout-plain-bullets-string
1977b8f6 3520 (% (max 0 (- depth 2))
fd5359c6
MR
3521 allout-plain-bullets-string-len)))
3522 allout-primary-bullet)
1977b8f6
RS
3523 )
3524
c567ac01 3525;;;_ - Topic Production
fd5359c6
MR
3526;;;_ > allout-make-topic-prefix (&optional prior-bullet
3527(defun allout-make-topic-prefix (&optional prior-bullet
1977b8f6
RS
3528 new
3529 depth
b6a5875b 3530 instead
1977b8f6
RS
3531 number-control
3532 index)
3533 ;; Depth null means use current depth, non-null means we're either
3534 ;; opening a new topic after current topic, lower or higher, or we're
3535 ;; changing level of current topic.
b6a5875b 3536 ;; Instead dominates specified bullet-char.
c567ac01 3537;;;_ . Doc string:
19b84ba3 3538 "Generate a topic prefix suitable for optional arg DEPTH, or current depth.
c567ac01
RS
3539
3540All the arguments are optional.
3541
3542PRIOR-BULLET indicates the bullet of the prefix being changed, or
3543nil if none. This bullet may be preserved (other options
2265e017 3544notwithstanding) if it is on the `allout-distinctive-bullets-string',
c567ac01
RS
3545for instance.
3546
3547Second arg NEW indicates that a new topic is being opened after the
3548topic at point, if non-nil. Default bullet for new topics, eg, may
3549be set (contingent to other args) to numbered bullets if previous
3550sibling is one. The implication otherwise is that the current topic
60c7bbd2 3551is being adjusted -- shifted or rebulleted -- and we don't consider
c567ac01
RS
3552bullet or previous sibling.
3553
3554Third arg DEPTH forces the topic prefix to that depth, regardless of
3555the current topics' depth.
3556
b6a5875b
KM
3557If INSTEAD is:
3558
3559- nil, then the bullet char for the context is used, per distinction or depth
20db1522 3560- a (numeric) character, then character's string representation is used
2dab465b 3561- a string, then the user is asked for bullet with the first char as default
b6a5875b
KM
3562- anything else, the user is solicited with bullet char per context as default
3563
3564\(INSTEAD overrides other options, including, eg, a distinctive
3565PRIOR-BULLET.)
c567ac01 3566
fd5359c6 3567Fifth arg, NUMBER-CONTROL, matters only if `allout-numbered-bullet'
b6a5875b 3568is non-nil *and* no specific INSTEAD was specified. Then
c567ac01 3569NUMBER-CONTROL non-nil forces prefix to either numbered or
4c36be58 3570unnumbered format, depending on the value of the sixth arg, INDEX.
c567ac01
RS
3571
3572\(Note that NUMBER-CONTROL does *not* apply to level 1 topics. Sorry...)
3573
3574If NUMBER-CONTROL is non-nil and sixth arg INDEX is non-nil then
3575the prefix of the topic is forced to be numbered. Non-nil
3576NUMBER-CONTROL and nil INDEX forces non-numbered format on the
3577bullet. Non-nil NUMBER-CONTROL and non-nil, non-number INDEX means
3578that the index for the numbered prefix will be derived, by counting
3579siblings back to start of level. If INDEX is a number, then that
3580number is used as the index for the numbered prefix (allowing, eg,
a0776d6b 3581sequential renumbering to not require this function counting back the
c567ac01
RS
3582index for each successive sibling)."
3583;;;_ . Code:
c5e87d10 3584 ;; The options are ordered in likely frequency of use, most common
1977b8f6
RS
3585 ;; highest, least lowest. Ie, more likely to be doing prefix
3586 ;; adjustments than soliciting, and yet more than numbering.
3587 ;; Current prefix is least dominant, but most likely to be commonly
3588 ;; specified...
3589
3590 (let* (body
3591 numbering
3592 denumbering
fd5359c6
MR
3593 (depth (or depth (allout-depth)))
3594 (header-lead allout-header-prefix)
1977b8f6
RS
3595 (bullet-char
3596
3597 ;; Getting value for bullet char is practically the whole job:
3598
3599 (cond
60c7bbd2 3600 ; Simplest situation -- level 1:
fd5359c6 3601 ((<= depth 1) (setq header-lead "") allout-primary-bullet)
1977b8f6 3602 ; Simple, too: all asterisks:
fd5359c6 3603 (allout-old-style-prefixes
60c7bbd2 3604 ;; Cheat -- make body the whole thing, null out header-lead and
1977b8f6
RS
3605 ;; bullet-char:
3606 (setq body (make-string depth
fd5359c6 3607 (string-to-char allout-primary-bullet)))
1977b8f6
RS
3608 (setq header-lead "")
3609 "")
3610
3611 ;; (Neither level 1 nor old-style, so we're space padding.
3612 ;; Sneak it in the condition of the next case, whatever it is.)
3613
3614 ;; Solicitation overrides numbering and other cases:
3615 ((progn (setq body (make-string (- depth 2) ?\ ))
3616 ;; The actual condition:
b6a5875b 3617 instead)
2dab465b
KM
3618 (let ((got (cond ((stringp instead)
3619 (if (> (length instead) 0)
3620 (allout-solicit-alternate-bullet
3621 depth (substring instead 0 1))))
3622 ((characterp instead) (char-to-string instead))
3623 (t (allout-solicit-alternate-bullet depth)))))
1977b8f6 3624 ;; Gotta check whether we're numbering and got a numbered bullet:
fd5359c6 3625 (setq numbering (and allout-numbered-bullet
1977b8f6 3626 (not (and number-control (not index)))
fd5359c6 3627 (string= got allout-numbered-bullet)))
1977b8f6
RS
3628 ;; Now return what we got, regardless:
3629 got))
3630
3631 ;; Numbering invoked through args:
fd5359c6 3632 ((and allout-numbered-bullet number-control)
1977b8f6 3633 (if (setq numbering (not (setq denumbering (not index))))
fd5359c6 3634 allout-numbered-bullet
19b84ba3 3635 (if (and prior-bullet
fd5359c6 3636 (not (string= allout-numbered-bullet
19b84ba3
RS
3637 prior-bullet)))
3638 prior-bullet
fd5359c6 3639 (allout-bullet-for-depth depth))))
1977b8f6
RS
3640
3641 ;;; Neither soliciting nor controlled numbering ;;;
3642 ;;; (may be controlled denumbering, tho) ;;;
3643
3644 ;; Check wrt previous sibling:
3645 ((and new ; only check for new prefixes
fd5359c6
MR
3646 (<= depth (allout-depth))
3647 allout-numbered-bullet ; ... & numbering enabled
1977b8f6
RS
3648 (not denumbering)
3649 (let ((sibling-bullet
3650 (save-excursion
3651 ;; Locate correct sibling:
fd5359c6
MR
3652 (or (>= depth (allout-depth))
3653 (allout-ascend-to-depth depth))
3654 (allout-get-bullet))))
1977b8f6 3655 (if (and sibling-bullet
fd5359c6 3656 (string= allout-numbered-bullet sibling-bullet))
1977b8f6
RS
3657 (setq numbering sibling-bullet)))))
3658
3659 ;; Distinctive prior bullet?
3660 ((and prior-bullet
fd5359c6 3661 (allout-distinctive-bullet prior-bullet)
1977b8f6 3662 ;; Either non-numbered:
fd5359c6
MR
3663 (or (not (and allout-numbered-bullet
3664 (string= prior-bullet allout-numbered-bullet)))
1977b8f6
RS
3665 ;; or numbered, and not denumbering:
3666 (setq numbering (not denumbering)))
3667 ;; Here 'tis:
3668 prior-bullet))
3669
3670 ;; Else, standard bullet per depth:
fd5359c6 3671 ((allout-bullet-for-depth depth)))))
1977b8f6
RS
3672
3673 (concat header-lead
3674 body
3675 bullet-char
3676 (if numbering
3677 (format "%d" (cond ((and index (numberp index)) index)
fd5359c6
MR
3678 (new (1+ (allout-sibling-index depth)))
3679 ((allout-sibling-index))))))
1977b8f6
RS
3680 )
3681 )
96b83743
EZ
3682;;;_ > allout-open-topic (relative-depth &optional before offer-recent-bullet)
3683(defun allout-open-topic (relative-depth &optional before offer-recent-bullet)
d82979ea 3684 "Open a new topic at depth DEPTH.
19b84ba3
RS
3685
3686New topic is situated after current one, unless optional flag BEFORE
60c7bbd2
RS
3687is non-nil, or unless current line is completely empty -- lacking even
3688whitespace -- in which case open is done on the current line.
96b83743
EZ
3689
3690When adding an offspring, it will be added immediately after the parent if
3691the other offspring are exposed, or after the last child if the offspring
c07583cd 3692are hidden. (The intervening offspring will be exposed in the latter
96b83743 3693case.)
c567ac01 3694
96b83743 3695If OFFER-RECENT-BULLET is true, offer to use the bullet of the prior sibling.
9179616f 3696
c567ac01
RS
3697Nuances:
3698
3699- Creation of new topics is with respect to the visible topic
3700 containing the cursor, regardless of intervening concealed ones.
3701
3702- New headers are generally created after/before the body of a
3703 topic. However, they are created right at cursor location if the
3704 cursor is on a blank line, even if that breaks the current topic
3705 body. This is intentional, to provide a simple means for
3706 deliberately dividing topic bodies.
3707
3708- Double spacing of topic lists is preserved. Also, the first
3709 level two topic is created double-spaced (and so would be
3710 subsequent siblings, if that's left intact). Otherwise,
3711 single-spacing is used.
3712
3713- Creation of sibling or nested topics is with respect to the topic
3714 you're starting from, even when creating backwards. This way you
3715 can easily create a sibling in front of the current topic without
a0776d6b 3716 having to go to its preceding sibling, and then open forward
c567ac01 3717 from there."
1977b8f6 3718
96b83743 3719 (allout-beginning-of-current-line)
2a1408fd
DN
3720 (save-match-data
3721 (let* ((inhibit-field-text-motion t)
3722 (depth (+ (allout-current-depth) relative-depth))
3723 (opening-on-blank (if (looking-at "^\$")
3724 (not (setq before nil))))
3725 ;; bunch o vars set while computing ref-topic
3726 opening-numbered
3727 ref-depth
3728 ref-bullet
3729 (ref-topic (save-excursion
3730 (cond ((< relative-depth 0)
3731 (allout-ascend-to-depth depth))
3732 ((>= relative-depth 1) nil)
3733 (t (allout-back-to-current-heading)))
3734 (setq ref-depth allout-recent-depth)
3735 (setq ref-bullet
3736 (if (> allout-recent-prefix-end 1)
3737 (allout-recent-bullet)
3738 ""))
3739 (setq opening-numbered
3740 (save-excursion
3741 (and allout-numbered-bullet
3742 (or (<= relative-depth 0)
3743 (allout-descend-to-depth depth))
3744 (if (allout-numbered-type-prefix)
3745 allout-numbered-bullet))))
3746 (point)))
3747 dbl-space
3748 doing-beginning
3749 start end)
3750
3751 (if (not opening-on-blank)
1977b8f6 3752 ; Positioning and vertical
60c7bbd2 3753 ; padding -- only if not
1977b8f6 3754 ; opening-on-blank:
2a1408fd
DN
3755 (progn
3756 (goto-char ref-topic)
3757 (setq dbl-space ; Determine double space action:
3758 (or (and (<= relative-depth 0) ; not descending;
3759 (save-excursion
3760 ;; at b-o-b or preceded by a blank line?
3761 (or (> 0 (forward-line -1))
3762 (looking-at "^\\s-*$")
3763 (bobp)))
3764 (save-excursion
3765 ;; succeeded by a blank line?
3766 (allout-end-of-current-subtree)
3767 (looking-at "\n\n")))
3768 (and (= ref-depth 1)
3769 (or before
3770 (= depth 1)
3771 (save-excursion
3772 ;; Don't already have following
3773 ;; vertical padding:
3774 (not (allout-pre-next-prefix)))))))
3775
3776 ;; Position to prior heading, if inserting backwards, and not
3777 ;; going outwards:
3778 (if (and before (>= relative-depth 0))
3779 (progn (allout-back-to-current-heading)
3780 (setq doing-beginning (bobp))
3781 (if (not (bobp))
3782 (allout-previous-heading)))
3783 (if (and before (bobp))
3784 (open-line 1)))
3785
3786 (if (<= relative-depth 0)
3787 ;; Not going inwards, don't snug up:
3788 (if doing-beginning
3789 (if (not dbl-space)
3790 (open-line 1)
3791 (open-line 2))
3792 (if before
3793 (progn (end-of-line)
3794 (allout-pre-next-prefix)
3795 (while (and (= ?\n (following-char))
3796 (save-excursion
3797 (forward-char 1)
3798 (allout-hidden-p)))
3799 (forward-char 1))
3800 (if (not (looking-at "^$"))
3801 (open-line 1)))
3802 (allout-end-of-current-subtree)
3803 (if (looking-at "\n\n") (forward-char 1))))
8dd59f01 3804 ;; Going inwards -- double-space if first offspring is
2a1408fd
DN
3805 ;; double-spaced, otherwise snug up.
3806 (allout-end-of-entry)
3807 (if (eobp)
3808 (newline 1)
3809 (line-move 1))
3810 (allout-beginning-of-current-line)
3811 (backward-char 1)
3812 (if (bolp)
3813 ;; Blank lines between current header body and next
8dd59f01 3814 ;; header -- get to last substantive (non-white-space)
2a1408fd
DN
3815 ;; line in body:
3816 (progn (setq dbl-space t)
3817 (re-search-backward "[^ \t\n]" nil t)))
3818 (if (looking-at "\n\n")
3819 (setq dbl-space t))
3820 (if (save-excursion
3821 (allout-next-heading)
3822 (when (> allout-recent-depth ref-depth)
3823 ;; This is an offspring.
3824 (forward-line -1)
3825 (looking-at "^\\s-*$")))
3826 (progn (forward-line 1)
96b83743 3827 (open-line 1)
2a1408fd
DN
3828 (forward-line 1)))
3829 (allout-end-of-current-line))
3830
3831 ;;(if doing-beginning (goto-char doing-beginning))
3832 (if (not (bobp))
3833 ;; We insert a newline char rather than using open-line to
0b381c7e 3834 ;; avoid rear-stickiness inheritance of read-only property.
2a1408fd
DN
3835 (progn (if (and (not (> depth ref-depth))
3836 (not before))
3837 (open-line 1)
3838 (if (and (not dbl-space) (> depth ref-depth))
3839 (newline 1)
3840 (if dbl-space
3841 (open-line 1)
3842 (if (not before)
3843 (newline 1)))))
3844 (if (and dbl-space (not (> relative-depth 0)))
3845 (newline 1))
3846 (if (and (not (eobp))
3847 (or (not (bolp))
3848 (and (not (bobp))
c7015153 3849 ;; bolp doesn't detect concealed
2a1408fd
DN
3850 ;; trailing newlines, compensate:
3851 (save-excursion
3852 (forward-char -1)
3853 (allout-hidden-p)))))
3854 (forward-char 1))))
3855 ))
3856 (setq start (point))
3857 (insert (concat (allout-make-topic-prefix opening-numbered t depth)
3858 " "))
3859 (setq end (1+ (point)))
3860
3861 (allout-rebullet-heading (and offer-recent-bullet ref-bullet)
3862 depth nil nil t)
3863 (if (> relative-depth 0)
3864 (save-excursion (goto-char ref-topic)
3865 (allout-show-children)))
3866 (end-of-line)
48bd8440 3867
7b97c764 3868 (run-hook-with-args 'allout-structure-added-functions start end)
2a1408fd 3869 )
1977b8f6
RS
3870 )
3871 )
fd5359c6
MR
3872;;;_ > allout-open-subtopic (arg)
3873(defun allout-open-subtopic (arg)
c567ac01
RS
3874 "Open new topic header at deeper level than the current one.
3875
20db1522 3876Negative universal ARG means to open deeper, but place the new topic
c567ac01 3877prior to the current one."
1977b8f6 3878 (interactive "p")
d82979ea 3879 (allout-open-topic 1 (> 0 arg) (< 1 arg)))
fd5359c6
MR
3880;;;_ > allout-open-sibtopic (arg)
3881(defun allout-open-sibtopic (arg)
19b84ba3
RS
3882 "Open new topic header at same level as the current one.
3883
20db1522 3884Positive universal ARG means to use the bullet of the prior sibling.
9179616f 3885
20db1522 3886Negative universal ARG means to place the new topic prior to the current
c567ac01 3887one."
1977b8f6 3888 (interactive "p")
d82979ea 3889 (allout-open-topic 0 (> 0 arg) (not (= 1 arg))))
fd5359c6
MR
3890;;;_ > allout-open-supertopic (arg)
3891(defun allout-open-supertopic (arg)
c567ac01 3892 "Open new topic header at shallower level than the current one.
19b84ba3 3893
20db1522 3894Negative universal ARG means to open shallower, but place the new
c567ac01 3895topic prior to the current one."
1977b8f6
RS
3896
3897 (interactive "p")
d82979ea 3898 (allout-open-topic -1 (> 0 arg) (< 1 arg)))
c567ac01
RS
3899
3900;;;_ - Outline Alteration
3901;;;_ : Topic Modification
fd5359c6
MR
3902;;;_ = allout-former-auto-filler
3903(defvar allout-former-auto-filler nil
2265e017 3904 "Name of modal fill function being wrapped by `allout-auto-fill'.")
fd5359c6
MR
3905;;;_ > allout-auto-fill ()
3906(defun allout-auto-fill ()
aad94676 3907 "`allout-mode' autofill function.
19b84ba3
RS
3908
3909Maintains outline hanging topic indentation if
fd5359c6 3910`allout-use-hanging-indents' is set."
01fc9422 3911
381987c3
KM
3912 (when (and (not allout-inhibit-auto-fill)
3913 (or (not allout-inhibit-auto-fill-on-headline)
3914 (not (allout-on-current-heading-p))))
01fc9422
EZ
3915 (let ((fill-prefix (if allout-use-hanging-indents
3916 ;; Check for topic header indentation:
2a1408fd
DN
3917 (save-match-data
3918 (save-excursion
3919 (beginning-of-line)
3920 (if (looking-at allout-regexp)
3921 ;; ... construct indentation to account for
3922 ;; length of topic prefix:
3923 (make-string (progn (allout-end-of-prefix)
3924 (current-column))
3925 ?\ ))))))
7927f48d
KM
3926 (use-auto-fill-function
3927 (if (and (eq allout-outside-normal-auto-fill-function
3928 'allout-auto-fill)
3929 (eq auto-fill-function 'allout-auto-fill))
3930 'do-auto-fill
3931 (or allout-outside-normal-auto-fill-function
3932 auto-fill-function))))
01fc9422
EZ
3933 (if (or allout-former-auto-filler allout-use-hanging-indents)
3934 (funcall use-auto-fill-function)))))
fd5359c6 3935;;;_ > allout-reindent-body (old-depth new-depth &optional number)
9d3aa82c 3936(defun allout-reindent-body (old-depth new-depth &optional _number)
e126900f 3937 "Reindent body lines which were indented at OLD-DEPTH to NEW-DEPTH.
c567ac01
RS
3938
3939Optional arg NUMBER indicates numbering is being added, and it must
a0776d6b 3940be accommodated.
c567ac01
RS
3941
3942Note that refill of indented paragraphs is not done."
1977b8f6
RS
3943
3944 (save-excursion
fd5359c6 3945 (allout-end-of-prefix)
c567ac01
RS
3946 (let* ((new-margin (current-column))
3947 excess old-indent-begin old-indent-end
c567ac01
RS
3948 ;; We want the column where the header-prefix text started
3949 ;; *before* the prefix was changed, so we infer it relative
3950 ;; to the new margin and the shift in depth:
3951 (old-margin (+ old-depth (- new-margin new-depth))))
353e2ef2 3952
c567ac01 3953 ;; Process lines up to (but excluding) next topic header:
fd5359c6 3954 (allout-unprotected
c567ac01
RS
3955 (save-match-data
3956 (while
96b83743 3957 (and (re-search-forward "\n\\(\\s-*\\)"
c567ac01
RS
3958 nil
3959 t)
3960 ;; Register the indent data, before we reset the
353e2ef2 3961 ;; match data with a subsequent `looking-at':
c567ac01
RS
3962 (setq old-indent-begin (match-beginning 1)
3963 old-indent-end (match-end 1))
fd5359c6 3964 (not (looking-at allout-regexp)))
d82979ea
EZ
3965 (if (> 0 (setq excess (- (- old-indent-end old-indent-begin)
3966 old-margin)))
60c7bbd2 3967 ;; Text starts left of old margin -- don't adjust:
c567ac01 3968 nil
ea18b2b1 3969 ;; Text was hanging at or right of old left margin --
c567ac01
RS
3970 ;; reindent it, preserving its existing indentation
3971 ;; beyond the old margin:
3972 (delete-region old-indent-begin old-indent-end)
d82979ea 3973 (indent-to (+ new-margin excess (current-column))))))))))
fd5359c6
MR
3974;;;_ > allout-rebullet-current-heading (arg)
3975(defun allout-rebullet-current-heading (arg)
9179616f
DL
3976 "Solicit new bullet for current visible heading."
3977 (interactive "p")
3978 (let ((initial-col (current-column))
fd5359c6 3979 (on-bullet (eq (point)(allout-current-bullet-pos)))
d8350998 3980 from to
9179616f
DL
3981 (backwards (if (< arg 0)
3982 (setq arg (* arg -1)))))
3983 (while (> arg 0)
fd5359c6
MR
3984 (save-excursion (allout-back-to-current-heading)
3985 (allout-end-of-prefix)
d8350998
CY
3986 (setq from allout-recent-prefix-beginning
3987 to allout-recent-prefix-end)
b6a5875b 3988 (allout-rebullet-heading t ;;; instead
9179616f
DL
3989 nil ;;; depth
3990 nil ;;; number-control
3991 nil ;;; index
d8350998 3992 t) ;;; do-successors
7b97c764 3993 (run-hook-with-args 'allout-exposure-change-functions
d8350998 3994 from to t))
9179616f
DL
3995 (setq arg (1- arg))
3996 (if (<= arg 0)
3997 nil
3998 (setq initial-col nil) ; Override positioning back to init col
3999 (if (not backwards)
fd5359c6 4000 (allout-next-visible-heading 1)
d8350998 4001 (allout-goto-prefix-doublechecked)
fd5359c6 4002 (allout-next-visible-heading -1))))
9179616f 4003 (message "Done.")
fd5359c6 4004 (cond (on-bullet (goto-char (allout-current-bullet-pos)))
9179616f 4005 (initial-col (move-to-column initial-col)))))
b6a5875b
KM
4006;;;_ > allout-rebullet-heading (&optional instead ...)
4007(defun allout-rebullet-heading (&optional instead
1977b8f6
RS
4008 new-depth
4009 number-control
4010 index
4011 do-successors)
4012
c567ac01 4013 "Adjust bullet of current topic prefix.
1977b8f6 4014
d82979ea
EZ
4015All args are optional.
4016
b6a5875b
KM
4017If INSTEAD is:
4018- nil, then the bullet char for the context is used, per distinction or depth
20db1522 4019- a (numeric) character, then character's string representation is used
2dab465b 4020- a string, then the user is asked for bullet with the first char as default
b6a5875b 4021- anything else, the user is solicited with bullet char per context as default
1977b8f6 4022
d82979ea 4023Second arg DEPTH forces the topic prefix to that depth, regardless
9179616f 4024of the topic's current depth.
1977b8f6 4025
c567ac01 4026Third arg NUMBER-CONTROL can force the prefix to or away from
fd5359c6 4027numbered form. It has effect only if `allout-numbered-bullet' is
c567ac01
RS
4028non-nil and soliciting was not explicitly invoked (via first arg).
4029Its effect, numbering or denumbering, then depends on the setting
75bcb912 4030of the fourth arg, INDEX.
1977b8f6 4031
75bcb912 4032If NUMBER-CONTROL is non-nil and fourth arg INDEX is nil, then the
c567ac01
RS
4033prefix of the topic is forced to be non-numbered. Null index and
4034non-nil NUMBER-CONTROL forces denumbering. Non-nil INDEX (and
4035non-nil NUMBER-CONTROL) forces a numbered-prefix form. If non-nil
4036INDEX is a number, then that number is used for the numbered
4037prefix. Non-nil and non-number means that the index for the
d82979ea 4038numbered prefix will be derived by allout-make-topic-prefix.
1977b8f6 4039
c567ac01
RS
4040Fifth arg DO-SUCCESSORS t means re-resolve count on succeeding
4041siblings.
1977b8f6 4042
fd5359c6
MR
4043Cf vars `allout-stylish-prefixes', `allout-old-style-prefixes',
4044and `allout-numbered-bullet', which all affect the behavior of
c567ac01 4045this function."
1977b8f6 4046
fd5359c6 4047 (let* ((current-depth (allout-depth))
1977b8f6 4048 (new-depth (or new-depth current-depth))
fd5359c6
MR
4049 (mb allout-recent-prefix-beginning)
4050 (me allout-recent-prefix-end)
d8350998 4051 (current-bullet (buffer-substring-no-properties (- me 1) me))
ede4ac6a 4052 (has-annotation (get-text-property mb 'allout-was-hidden))
fd5359c6 4053 (new-prefix (allout-make-topic-prefix current-bullet
1977b8f6
RS
4054 nil
4055 new-depth
b6a5875b 4056 instead
1977b8f6
RS
4057 number-control
4058 index)))
4059
20db1522 4060 ;; Is new one identical to old?
1977b8f6
RS
4061 (if (and (= current-depth new-depth)
4062 (string= current-bullet
4063 (substring new-prefix (1- (length new-prefix)))))
c567ac01 4064 ;; Nothing to do:
1977b8f6
RS
4065 t
4066
4067 ;; New prefix probably different from old:
c567ac01 4068 ; get rid of old one:
fd5359c6 4069 (allout-unprotected (delete-region mb me))
1977b8f6 4070 (goto-char mb)
c567ac01
RS
4071 ; Dispense with number if
4072 ; numbered-bullet prefix:
2a1408fd
DN
4073 (save-match-data
4074 (if (and allout-numbered-bullet
4075 (string= allout-numbered-bullet current-bullet)
4076 (looking-at "[0-9]+"))
4077 (allout-unprotected
4078 (delete-region (match-beginning 0)(match-end 0)))))
1977b8f6 4079
ede4ac6a
KS
4080 ;; convey 'allout-was-hidden annotation, if original had it:
4081 (if has-annotation
4082 (put-text-property 0 (length new-prefix) 'allout-was-hidden t
4083 new-prefix))
4084
c567ac01 4085 ; Put in new prefix:
fd5359c6 4086 (allout-unprotected (insert new-prefix))
1977b8f6 4087
d82979ea 4088 ;; Reindent the body if elected, margin changed, and not encrypted body:
fd5359c6 4089 (if (and allout-reindent-bodies
d82979ea
EZ
4090 (not (= new-depth current-depth))
4091 (not (allout-encrypted-topic-p)))
fd5359c6 4092 (allout-reindent-body current-depth new-depth))
1977b8f6 4093
7b97c764 4094 (run-hook-with-args 'allout-exposure-change-functions mb me nil)
e47ca23b 4095
c567ac01
RS
4096 ;; Recursively rectify successive siblings of orig topic if
4097 ;; caller elected for it:
4098 (if do-successors
4099 (save-excursion
fd5359c6 4100 (while (allout-next-sibling new-depth nil)
c567ac01
RS
4101 (setq index
4102 (cond ((numberp index) (1+ index))
fd5359c6
MR
4103 ((not number-control) (allout-sibling-index))))
4104 (if (allout-numbered-type-prefix)
b6a5875b 4105 (allout-rebullet-heading nil ;;; instead
c567ac01
RS
4106 new-depth ;;; new-depth
4107 number-control;;; number-control
4108 index ;;; index
4109 nil))))) ;;;(dont!)do-successors
4110 ) ; (if (and (= current-depth new-depth)...))
fd5359c6 4111 ) ; let* ((current-depth (allout-depth))...)
c567ac01 4112 ) ; defun
fd5359c6 4113;;;_ > allout-rebullet-topic (arg)
d8350998 4114(defun allout-rebullet-topic (arg &optional sans-offspring)
d82979ea 4115 "Rebullet the visible topic containing point and all contained subtopics.
19b84ba3 4116
c567ac01 4117Descends into invisible as well as visible topics, however.
1977b8f6 4118
75bcb912 4119When optional SANS-OFFSPRING is non-nil, subtopics are not
c07583cd 4120shifted. (Shifting a topic outwards without shifting its
d8350998
CY
4121offspring is disallowed, since this would create a \"containment
4122discontinuity\", where the depth difference between a topic and
4123its immediate offspring is greater than one.)
4124
c567ac01 4125With repeat count, shift topic depth by that amount."
1977b8f6 4126 (interactive "P")
96b83743 4127 (let ((start-col (current-column)))
1977b8f6
RS
4128 (save-excursion
4129 ;; Normalize arg:
4130 (cond ((null arg) (setq arg 0))
4131 ((listp arg) (setq arg (car arg))))
4132 ;; Fill the user in, in case we're shifting a big topic:
4133 (if (not (zerop arg)) (message "Shifting..."))
fd5359c6 4134 (allout-back-to-current-heading)
d8350998 4135 (if (<= (+ allout-recent-depth arg) 0)
1977b8f6 4136 (error "Attempt to shift topic below level 1"))
d8350998 4137 (allout-rebullet-topic-grunt arg nil nil nil nil sans-offspring)
1977b8f6 4138 (if (not (zerop arg)) (message "Shifting... done.")))
c567ac01 4139 (move-to-column (max 0 (+ start-col arg)))))
d8350998 4140;;;_ > allout-rebullet-topic-grunt (&optional relative-depth ...)
fd5359c6 4141(defun allout-rebullet-topic-grunt (&optional relative-depth
1977b8f6
RS
4142 starting-depth
4143 starting-point
4144 index
d8350998
CY
4145 do-successors
4146 sans-offspring)
d82979ea
EZ
4147 "Like `allout-rebullet-topic', but on nearest containing topic
4148\(visible or not).
1977b8f6 4149
d82979ea 4150See `allout-rebullet-heading' for rebulleting behavior.
1977b8f6 4151
d82979ea 4152All arguments are optional.
1977b8f6 4153
d82979ea
EZ
4154First arg RELATIVE-DEPTH means to shift the depth of the entire
4155topic that amount.
e126900f 4156
d8350998
CY
4157Several subsequent args are for internal recursive use by the function
4158itself: STARTING-DEPTH, STARTING-POINT, and INDEX.
4159
4160Finally, if optional SANS-OFFSPRING is non-nil then the offspring
c07583cd 4161are not shifted. (Shifting a topic outwards without shifting
d8350998
CY
4162its offspring is disallowed, since this would create a
4163\"containment discontinuity\", where the depth difference between
75bcb912 4164a topic and its immediate offspring is greater than one.)"
d8350998
CY
4165
4166 ;; XXX the recursion here is peculiar, and in general the routine may
4167 ;; need simplification with refactoring.
4168
4169 (if (and sans-offspring
4170 relative-depth
4171 (< relative-depth 0))
4172 (error (concat "Attempt to shift topic outwards without offspring,"
4173 " would cause containment discontinuity.")))
1977b8f6
RS
4174
4175 (let* ((relative-depth (or relative-depth 0))
fd5359c6 4176 (new-depth (allout-depth))
1977b8f6
RS
4177 (starting-depth (or starting-depth new-depth))
4178 (on-starting-call (null starting-point))
4179 (index (or index
4180 ;; Leave index null on starting call, so rebullet-heading
4181 ;; calculates it at what might be new depth:
4182 (and (or (zerop relative-depth)
4183 (not on-starting-call))
fd5359c6 4184 (allout-sibling-index))))
d8350998 4185 (starting-index index)
1977b8f6 4186 (moving-outwards (< 0 relative-depth))
d8350998
CY
4187 (starting-point (or starting-point (point)))
4188 (local-point (point)))
1977b8f6
RS
4189
4190 ;; Sanity check for excessive promotion done only on starting call:
4191 (and on-starting-call
4192 moving-outwards
4193 (> 0 (+ starting-depth relative-depth))
d8350998 4194 (error "Attempt to shift topic out beyond level 1"))
1977b8f6
RS
4195
4196 (cond ((= starting-depth new-depth)
d8350998
CY
4197 ;; We're at depth to work on this one.
4198
4199 ;; When shifting out we work on the children before working on
4200 ;; the parent to avoid interim `allout-aberrant-container-p'
4201 ;; aberrancy, and vice-versa when shifting in:
4202 (if (>= relative-depth 0)
4203 (allout-rebullet-heading nil
4204 (+ starting-depth relative-depth)
4205 nil ;;; number
4206 index
4207 nil)) ;;; do-successors
4208 (when (not sans-offspring)
4209 ;; ... and work on subsequent ones which are at greater depth:
4210 (setq index 0)
4211 (allout-next-heading)
4212 (while (and (not (eobp))
4213 (< starting-depth (allout-depth)))
4214 (setq index (1+ index))
4215 (allout-rebullet-topic-grunt relative-depth
4216 (1+ starting-depth)
4217 starting-point
4218 index)))
4219 (when (< relative-depth 0)
4220 (save-excursion
4221 (goto-char local-point)
b6a5875b 4222 (allout-rebullet-heading nil ;;; instead
d8350998
CY
4223 (+ starting-depth relative-depth)
4224 nil ;;; number
4225 starting-index
4226 nil)))) ;;; do-successors
1977b8f6
RS
4227
4228 ((< starting-depth new-depth)
60c7bbd2 4229 ;; Rare case -- subtopic more than one level deeper than parent.
1977b8f6 4230 ;; Treat this one at an even deeper level:
d8350998
CY
4231 (allout-rebullet-topic-grunt relative-depth
4232 new-depth
4233 starting-point
4234 index
4235 sans-offspring)))
1977b8f6
RS
4236
4237 (if on-starting-call
4238 (progn
4239 ;; Rectify numbering of former siblings of the adjusted topic,
4240 ;; if topic has changed depth
4241 (if (or do-successors
4242 (and (not (zerop relative-depth))
d8350998
CY
4243 (or (= allout-recent-depth starting-depth)
4244 (= allout-recent-depth (+ starting-depth
1977b8f6 4245 relative-depth)))))
fd5359c6 4246 (allout-rebullet-heading nil nil nil nil t))
1977b8f6
RS
4247 ;; Now rectify numbering of new siblings of the adjusted topic,
4248 ;; if depth has been changed:
4249 (progn (goto-char starting-point)
4250 (if (not (zerop relative-depth))
fd5359c6 4251 (allout-rebullet-heading nil nil nil nil t)))))
1977b8f6
RS
4252 )
4253 )
fd5359c6
MR
4254;;;_ > allout-renumber-to-depth (&optional depth)
4255(defun allout-renumber-to-depth (&optional depth)
19b84ba3
RS
4256 "Renumber siblings at current depth.
4257
4258Affects superior topics if optional arg DEPTH is less than current depth.
c567ac01
RS
4259
4260Returns final depth."
4261
4262 ;; Proceed by level, processing subsequent siblings on each,
4263 ;; ascending until we get shallower than the start depth:
4264
fd5359c6 4265 (let ((ascender (allout-depth))
9179616f 4266 was-eobp)
c567ac01 4267 (while (and (not (eobp))
fd5359c6 4268 (allout-depth)
d8350998 4269 (>= allout-recent-depth depth)
c567ac01
RS
4270 (>= ascender depth))
4271 ; Skip over all topics at
4272 ; lesser depths, which can not
4273 ; have been disturbed:
9179616f 4274 (while (and (not (setq was-eobp (eobp)))
d8350998 4275 (> allout-recent-depth ascender))
fd5359c6 4276 (allout-next-heading))
c567ac01 4277 ; Prime ascender for ascension:
d8350998
CY
4278 (setq ascender (1- allout-recent-depth))
4279 (if (>= allout-recent-depth depth)
b6a5875b 4280 (allout-rebullet-heading nil ;;; instead
c567ac01
RS
4281 nil ;;; depth
4282 nil ;;; number-control
4283 nil ;;; index
9179616f
DL
4284 t)) ;;; do-successors
4285 (if was-eobp (goto-char (point-max)))))
d8350998 4286 allout-recent-depth)
fd5359c6
MR
4287;;;_ > allout-number-siblings (&optional denumber)
4288(defun allout-number-siblings (&optional denumber)
c567ac01 4289 "Assign numbered topic prefix to this topic and its siblings.
1977b8f6 4290
60c7bbd2 4291With universal argument, denumber -- assign default bullet to this
c567ac01 4292topic and its siblings.
1977b8f6 4293
c567ac01
RS
4294With repeated universal argument (`^U^U'), solicit bullet for each
4295rebulleting each topic at this level."
1977b8f6
RS
4296
4297 (interactive "P")
4298
4299 (save-excursion
fd5359c6
MR
4300 (allout-back-to-current-heading)
4301 (allout-beginning-of-level)
d8350998 4302 (let ((depth allout-recent-depth)
c567ac01 4303 (index (if (not denumber) 1))
1977b8f6
RS
4304 (use-bullet (equal '(16) denumber))
4305 (more t))
4306 (while more
b6a5875b 4307 (allout-rebullet-heading use-bullet ;;; instead
c567ac01 4308 depth ;;; depth
1977b8f6
RS
4309 t ;;; number-control
4310 index ;;; index
4311 nil) ;;; do-successors
4312 (if index (setq index (1+ index)))
fd5359c6
MR
4313 (setq more (allout-next-sibling depth nil))))))
4314;;;_ > allout-shift-in (arg)
4315(defun allout-shift-in (arg)
d8350998
CY
4316 "Increase depth of current heading and any items collapsed within it.
4317
4318With a negative argument, the item is shifted out using
4319`allout-shift-out', instead.
4320
4321With an argument greater than one, shift-in the item but not its
4322offspring, making the item into a sibling of its former children,
97610156 4323and a child of sibling that formerly preceded it.
d8350998
CY
4324
4325You are not allowed to shift the first offspring of a topic
4326inwards, because that would yield a \"containment
4327discontinuity\", where the depth difference between a topic and
4328its immediate offspring is greater than one. The first topic in
4329the file can be adjusted to any positive depth, however."
d82979ea 4330
1977b8f6 4331 (interactive "p")
d8350998
CY
4332 (if (< arg 0)
4333 (allout-shift-out (* arg -1))
4334 ;; refuse to create a containment discontinuity:
4335 (save-excursion
4336 (allout-back-to-current-heading)
4337 (if (not (bobp))
4338 (let* ((current-depth allout-recent-depth)
4339 (start-point (point))
4340 (predecessor-depth (progn
4341 (forward-char -1)
4342 (allout-goto-prefix-doublechecked)
4343 (if (< (point) start-point)
4344 allout-recent-depth
4345 0))))
4346 (if (and (> predecessor-depth 0)
4347 (> (1+ current-depth)
4348 (1+ predecessor-depth)))
4349 (error (concat "Disallowed shift deeper than"
498a6493
CY
4350 " containing topic's children."))
4351 (allout-back-to-current-heading)
4352 (if (< allout-recent-depth (1+ current-depth))
4353 (allout-show-children))))))
d8350998
CY
4354 (let ((where (point)))
4355 (allout-rebullet-topic 1 (and (> arg 1) 'sans-offspring))
7b97c764 4356 (run-hook-with-args 'allout-structure-shifted-functions arg where))))
fd5359c6
MR
4357;;;_ > allout-shift-out (arg)
4358(defun allout-shift-out (arg)
d82979ea 4359 "Decrease depth of current heading and any topics collapsed within it.
d8350998
CY
4360This will make the item a sibling of its former container.
4361
4362With a negative argument, the item is shifted in using
4363`allout-shift-in', instead.
4364
4365With an argument greater than one, shift-out the item's offspring
4366but not the item itself, making the former children siblings of
4367the item.
d82979ea 4368
d8350998
CY
4369With an argument greater than 1, the item's offspring are shifted
4370out without shifting the item. This will make the immediate
4371subtopics into siblings of the item."
1977b8f6 4372 (interactive "p")
d8350998
CY
4373 (if (< arg 0)
4374 (allout-shift-in (* arg -1))
4375 ;; Get proper exposure in this area:
4376 (save-excursion (if (allout-ascend)
4377 (allout-show-children)))
4378 ;; Show collapsed children if there's a successor which will become
4379 ;; their sibling:
4380 (if (and (allout-current-topic-collapsed-p)
4381 (save-excursion (allout-next-sibling)))
4382 (allout-show-children))
4383 (let ((where (and (allout-depth) allout-recent-prefix-beginning)))
4384 (save-excursion
4385 (if (> arg 1)
4386 ;; Shift the offspring but not the topic:
4387 (let ((children-chart (allout-chart-subtree 1)))
4388 (if (listp (car children-chart))
4389 ;; whoops:
4390 (setq children-chart (allout-flatten children-chart)))
4391 (save-excursion
4392 (dolist (child-point children-chart)
4393 (goto-char child-point)
4394 (allout-shift-out 1))))
4395 (allout-rebullet-topic (* arg -1))))
7b97c764 4396 (run-hook-with-args 'allout-structure-shifted-functions (* arg -1) where))))
c567ac01 4397;;;_ : Surgery (kill-ring) functions with special provisions for outlines:
fd5359c6
MR
4398;;;_ > allout-kill-line (&optional arg)
4399(defun allout-kill-line (&optional arg)
c567ac01 4400 "Kill line, adjusting subsequent lines suitably for outline mode."
1977b8f6
RS
4401
4402 (interactive "*P")
d82979ea 4403
96b83743
EZ
4404 (if (or (not (allout-mode-p))
4405 (not (bolp))
2a1408fd 4406 (not (save-match-data (looking-at allout-regexp))))
4034b0e2 4407 ;; Just do a regular kill:
96b83743
EZ
4408 (kill-line arg)
4409 ;; Ah, have to watch out for adjustments:
4410 (let* ((beg (point))
d8350998 4411 end
96b83743
EZ
4412 (beg-hidden (allout-hidden-p))
4413 (end-hidden (save-excursion (allout-end-of-current-line)
d8350998 4414 (setq end (point))
96b83743 4415 (allout-hidden-p)))
d8350998 4416 (depth (allout-depth)))
96b83743 4417
d8350998 4418 (allout-annotate-hidden beg end)
4ec28e14
KM
4419 (unwind-protect
4420 (if (and (not beg-hidden) (not end-hidden))
4421 (allout-unprotected (kill-line arg))
4422 (kill-line arg))
11c46e15 4423 (run-hooks 'allout-after-copy-or-kill-hook)
4ec28e14
KM
4424 (allout-deannotate-hidden beg end)
4425
4426 (if allout-numbered-bullet
4427 (save-excursion ; Renumber subsequent topics if needed:
4428 (if (not (save-match-data (looking-at allout-regexp)))
4429 (allout-next-heading))
4430 (allout-renumber-to-depth depth)))
7b97c764 4431 (run-hook-with-args 'allout-structure-deleted-functions depth (point))))))
1b4d97b2
CY
4432;;;_ > allout-copy-line-as-kill ()
4433(defun allout-copy-line-as-kill ()
20db1522 4434 "Like `allout-kill-topic', but save to kill ring instead of deleting."
1b4d97b2
CY
4435 (interactive)
4436 (let ((buffer-read-only t))
4437 (condition-case nil
4438 (allout-kill-line)
4439 (buffer-read-only nil))))
fd5359c6
MR
4440;;;_ > allout-kill-topic ()
4441(defun allout-kill-topic ()
c567ac01
RS
4442 "Kill topic together with subtopics.
4443
96b83743
EZ
4444Trailing whitespace is killed with a topic if that whitespace:
4445
4446 - would separate the topic from a subsequent sibling
4447 - would separate the topic from the end of buffer
4448 - would not be added to whitespace already separating the topic from the
4449 previous one.
4450
d8350998 4451Topic exposure is marked with text-properties, to be used by
75bcb912 4452`allout-yank-processing' for exposure recovery."
1977b8f6
RS
4453
4454 (interactive)
0458fa80 4455 (let* ((inhibit-field-text-motion t)
96b83743 4456 (beg (prog1 (allout-back-to-current-heading) (beginning-of-line)))
ede4ac6a 4457 end
d8350998 4458 (depth allout-recent-depth))
fd5359c6 4459 (allout-end-of-current-subtree)
96b83743
EZ
4460 (if (and (/= (current-column) 0) (not (eobp)))
4461 (forward-char 1))
1977b8f6 4462 (if (not (eobp))
2a1408fd 4463 (if (and (save-match-data (looking-at "\n"))
96b83743
EZ
4464 (or (save-excursion
4465 (or (not (allout-next-heading))
d8350998 4466 (= depth allout-recent-depth)))
96b83743
EZ
4467 (and (> (- beg (point-min)) 3)
4468 (string= (buffer-substring (- beg 2) beg) "\n\n"))))
c567ac01 4469 (forward-char 1)))
353e2ef2 4470
ede4ac6a 4471 (allout-annotate-hidden beg (setq end (point)))
4ec28e14 4472 (unwind-protect ; for possible barf-if-buffer-read-only.
ede4ac6a 4473 (allout-unprotected (kill-region beg end))
4ec28e14 4474 (allout-deannotate-hidden beg end)
11c46e15 4475 (run-hooks 'allout-after-copy-or-kill-hook)
d8350998 4476
4ec28e14
KM
4477 (save-excursion
4478 (allout-renumber-to-depth depth))
7b97c764 4479 (run-hook-with-args 'allout-structure-deleted-functions depth (point)))))
1b4d97b2
CY
4480;;;_ > allout-copy-topic-as-kill ()
4481(defun allout-copy-topic-as-kill ()
75bcb912 4482 "Like `allout-kill-topic', but save to kill ring instead of deleting."
1b4d97b2
CY
4483 (interactive)
4484 (let ((buffer-read-only t))
4485 (condition-case nil
4486 (allout-kill-topic)
4487 (buffer-read-only (message "Topic copied...")))))
d8350998
CY
4488;;;_ > allout-annotate-hidden (begin end)
4489(defun allout-annotate-hidden (begin end)
4490 "Qualify text with properties to indicate exposure status."
4491
1b4d97b2
CY
4492 (let ((was-modified (buffer-modified-p))
4493 (buffer-read-only nil))
ede4ac6a 4494 (allout-deannotate-hidden begin end)
d8350998
CY
4495 (save-excursion
4496 (goto-char begin)
4497 (let (done next prev overlay)
4498 (while (not done)
4499 ;; at or advance to start of next hidden region:
4500 (if (not (allout-hidden-p))
4501 (setq next
1b4d97b2 4502 (max (1+ (point))
1c9b9df0
KM
4503 (allout-next-single-char-property-change (point)
4504 'invisible
4505 nil end))))
d8350998 4506 (if (or (not next) (eq prev next))
60c7bbd2 4507 ;; still not at start of hidden area -- must not be any left.
d8350998
CY
4508 (setq done t)
4509 (goto-char next)
4510 (setq prev next)
4511 (if (not (allout-hidden-p))
4512 ;; still not at start of hidden area.
4513 (setq done t)
4514 (setq overlay (allout-get-invisibility-overlay))
4515 (setq next (overlay-end overlay)
4516 prev next)
4517 ;; advance to end of this hidden area:
4518 (when next
4519 (goto-char next)
4520 (allout-unprotected
ede4ac6a
KS
4521 (let ((buffer-undo-list t))
4522 (put-text-property (overlay-start overlay) next
4523 'allout-was-hidden t)))))))))
d8350998 4524 (set-buffer-modified-p was-modified)))
ede4ac6a
KS
4525;;;_ > allout-deannotate-hidden (begin end)
4526(defun allout-deannotate-hidden (begin end)
4527 "Remove allout hidden-text annotation between BEGIN and END."
4528
4529 (allout-unprotected
4530 (let ((inhibit-read-only t)
4531 (buffer-undo-list t))
11c46e15
KM
4532 (remove-text-properties begin (min end (point-max))
4533 '(allout-was-hidden t)))))
d8350998
CY
4534;;;_ > allout-hide-by-annotation (begin end)
4535(defun allout-hide-by-annotation (begin end)
4536 "Translate text properties indicating exposure status into actual exposure."
4537 (save-excursion
4538 (goto-char begin)
4539 (let ((was-modified (buffer-modified-p))
4540 done next prev)
4541 (while (not done)
4542 ;; at or advance to start of next annotation:
4543 (if (not (get-text-property (point) 'allout-was-hidden))
1c9b9df0
KM
4544 (setq next (allout-next-single-char-property-change
4545 (point) 'allout-was-hidden nil end)))
d8350998 4546 (if (or (not next) (eq prev next))
60c7bbd2 4547 ;; no more or not advancing -- must not be any left.
d8350998
CY
4548 (setq done t)
4549 (goto-char next)
4550 (setq prev next)
4551 (if (not (get-text-property (point) 'allout-was-hidden))
4552 ;; still not at start of annotation.
4553 (setq done t)
4554 ;; advance to just after end of this annotation:
1c9b9df0
KM
4555 (setq next (allout-next-single-char-property-change
4556 (point) 'allout-was-hidden nil end))
d806ab68
KM
4557 (let ((o (make-overlay prev next nil 'front-advance)))
4558 (overlay-put o 'category 'allout-exposure-category)
4559 (overlay-put o 'evaporate t))
ede4ac6a 4560 (allout-deannotate-hidden prev next)
d8350998
CY
4561 (setq prev next)
4562 (if next (goto-char next)))))
4563 (set-buffer-modified-p was-modified))))
fd5359c6 4564;;;_ > allout-yank-processing ()
9d3aa82c 4565(defun allout-yank-processing (&optional _arg)
c567ac01 4566
96b83743 4567 "Incidental allout-specific business to be done just after text yanks.
19b84ba3 4568
c567ac01 4569Does depth adjustment of yanked topics, when:
1977b8f6 4570
c567ac01
RS
45711 the stuff being yanked starts with a valid outline header prefix, and
45722 it is being yanked at the end of a line which consists of only a valid
1977b8f6
RS
4573 topic prefix.
4574
a0776d6b 4575Also, adjusts numbering of subsequent siblings when appropriate.
1977b8f6 4576
c567ac01
RS
4577Depth adjustment alters the depth of all the topics being yanked
4578the amount it takes to make the first topic have the depth of the
4579header into which it's being yanked.
1977b8f6 4580
c567ac01
RS
4581The point is left in front of yanked, adjusted topics, rather than
4582at the end (and vice-versa with the mark). Non-adjusted yanks,
d82979ea 4583however, are left exactly like normal, non-allout-specific yanks."
c567ac01
RS
4584
4585 (interactive "*P")
4586 ; Get to beginning, leaving
4587 ; region around subject:
96b83743 4588 (if (< (allout-mark-marker t) (point))
c567ac01 4589 (exchange-point-and-mark))
2a1408fd
DN
4590 (save-match-data
4591 (let* ((subj-beg (point))
4592 (into-bol (bolp))
4593 (subj-end (allout-mark-marker t))
4594 ;; 'resituate' if yanking an entire topic into topic header:
4595 (resituate (and (let ((allout-inhibit-aberrance-doublecheck t))
4596 (allout-e-o-prefix-p))
4597 (looking-at allout-regexp)
4598 (allout-prefix-data)))
4599 ;; `rectify-numbering' if resituating (where several topics may
4600 ;; be resituating) or yanking a topic into a topic slot (bol):
4601 (rectify-numbering (or resituate
8dd59f01 4602 (and into-bol (looking-at allout-regexp)))))
2a1408fd 4603 (if resituate
8dd59f01 4604 ;; Yanking a topic into the start of a topic -- reconcile to fit:
2a1408fd
DN
4605 (let* ((inhibit-field-text-motion t)
4606 (prefix-len (if (not (match-end 1))
4607 1
4608 (- (match-end 1) subj-beg)))
4609 (subj-depth allout-recent-depth)
4610 (prefix-bullet (allout-recent-bullet))
4611 (adjust-to-depth
4612 ;; Nil if adjustment unnecessary, otherwise depth to which
4613 ;; adjustment should be made:
4614 (save-excursion
4615 (and (goto-char subj-end)
4616 (eolp)
4617 (goto-char subj-beg)
4618 (and (looking-at allout-regexp)
4619 (progn
4620 (beginning-of-line)
4621 (not (= (point) subj-beg)))
4622 (looking-at allout-regexp)
4623 (allout-prefix-data))
4624 allout-recent-depth)))
4625 (more t))
4626 (setq rectify-numbering allout-numbered-bullet)
4627 (if adjust-to-depth
c567ac01 4628 ; Do the adjustment:
2a1408fd
DN
4629 (progn
4630 (save-restriction
4631 (narrow-to-region subj-beg subj-end)
c567ac01
RS
4632 ; Trim off excessive blank
4633 ; line at end, if any:
2a1408fd
DN
4634 (goto-char (point-max))
4635 (if (looking-at "^$")
4636 (allout-unprotected (delete-char -1)))
c567ac01
RS
4637 ; Work backwards, with each
4638 ; shallowest level,
4639 ; successively excluding the
4640 ; last processed topic from
4641 ; the narrow region:
2a1408fd
DN
4642 (while more
4643 (allout-back-to-current-heading)
c567ac01 4644 ; go as high as we can in each bunch:
2a1408fd
DN
4645 (while (allout-ascend t))
4646 (save-excursion
4647 (allout-unprotected
4648 (allout-rebullet-topic-grunt (- adjust-to-depth
4649 subj-depth)))
4650 (allout-depth))
4651 (if (setq more (not (bobp)))
4652 (progn (widen)
4653 (forward-char -1)
4654 (narrow-to-region subj-beg (point))))))
b6a5875b
KM
4655 ;; Remove new heading prefix:
4656 (allout-unprotected
4657 (progn
4658 (delete-region (point) (+ (point)
4659 prefix-len
4660 (- adjust-to-depth
4661 subj-depth)))
c567ac01
RS
4662 ; and delete residual subj
4663 ; prefix digits and space:
b6a5875b 4664 (while (looking-at "[0-9]") (delete-char 1))
e47ca23b
KM
4665 (delete-char -1)
4666 (if (not (eolp))
4667 (forward-char))))
b6a5875b 4668 ;; Assert new topic's bullet - minimal effort if unchanged:
b0911414 4669 (allout-rebullet-heading (string-to-char prefix-bullet)))
2a1408fd
DN
4670 (exchange-point-and-mark))))
4671 (if rectify-numbering
4672 (progn
4673 (save-excursion
c567ac01 4674 ; Give some preliminary feedback:
2a1408fd 4675 (message "... reconciling numbers")
c567ac01 4676 ; ... and renumber, in case necessary:
2a1408fd
DN
4677 (goto-char subj-beg)
4678 (if (allout-goto-prefix-doublechecked)
4679 (allout-unprotected
b6a5875b 4680 (allout-rebullet-heading nil ;;; instead
2a1408fd
DN
4681 (allout-depth) ;;; depth
4682 nil ;;; number-control
4683 nil ;;; index
4684 t)))
4685 (message ""))))
4686 (if (or into-bol resituate)
4687 (allout-hide-by-annotation (point) (allout-mark-marker t))
4688 (allout-deannotate-hidden (allout-mark-marker t) (point)))
4689 (if (not resituate)
4690 (exchange-point-and-mark))
7b97c764 4691 (run-hook-with-args 'allout-structure-added-functions subj-beg subj-end))))
fd5359c6
MR
4692;;;_ > allout-yank (&optional arg)
4693(defun allout-yank (&optional arg)
aad94676 4694 "`allout-mode' yank, with depth and numbering adjustment of yanked topics.
19b84ba3 4695
a0776d6b 4696Non-topic yanks work no differently than normal yanks.
c567ac01
RS
4697
4698If a topic is being yanked into a bare topic prefix, the depth of the
4699yanked topic is adjusted to the depth of the topic prefix.
4700
2265e017 4701 1 we're yanking in an `allout-mode' buffer
c567ac01
RS
4702 2 the stuff being yanked starts with a valid outline header prefix, and
4703 3 it is being yanked at the end of a line which consists of only a valid
4704 topic prefix.
4705
4706If these conditions hold then the depth of the yanked topics are all
4707adjusted the amount it takes to make the first one at the depth of the
4708header into which it's being yanked.
4709
4710The point is left in front of yanked, adjusted topics, rather than
4711at the end (and vice-versa with the mark). Non-adjusted yanks,
4712however, (ones that don't qualify for adjustment) are handled
4713exactly like normal yanks.
4714
a0776d6b 4715Numbering of yanked topics, and the successive siblings at the depth
c567ac01
RS
4716into which they're being yanked, is adjusted.
4717
539d7736
JB
4718`allout-yank-pop' works with `allout-yank' just like normal `yank-pop'
4719works with normal `yank' in non-outline buffers."
1977b8f6
RS
4720
4721 (interactive "*P")
4722 (setq this-command 'yank)
48bd8440
EZ
4723 (allout-unprotected
4724 (yank arg))
fd5359c6 4725 (if (allout-mode-p)
48bd8440 4726 (allout-yank-processing)))
fd5359c6
MR
4727;;;_ > allout-yank-pop (&optional arg)
4728(defun allout-yank-pop (&optional arg)
2265e017 4729 "Yank-pop like `allout-yank' when popping to bare outline prefixes.
19b84ba3
RS
4730
4731Adapts level of popped topics to level of fresh prefix.
c567ac01 4732
60c7bbd2 4733Note -- prefix changes to distinctive bullets will stick, if followed
c567ac01 4734by pops to non-distinctive yanks. Bug..."
1977b8f6
RS
4735
4736 (interactive "*p")
1977b8f6 4737 (setq this-command 'yank)
c567ac01 4738 (yank-pop arg)
fd5359c6
MR
4739 (if (allout-mode-p)
4740 (allout-yank-processing)))
1977b8f6 4741
c567ac01
RS
4742;;;_ - Specialty bullet functions
4743;;;_ : File Cross references
fd5359c6
MR
4744;;;_ > allout-resolve-xref ()
4745(defun allout-resolve-xref ()
19b84ba3
RS
4746 "Pop to file associated with current heading, if it has an xref bullet.
4747
fd5359c6 4748\(Works according to setting of `allout-file-xref-bullet')."
1977b8f6 4749 (interactive)
fd5359c6 4750 (if (not allout-file-xref-bullet)
1977b8f6 4751 (error
60c7bbd2 4752 "Outline cross references disabled -- no `allout-file-xref-bullet'")
fd5359c6 4753 (if (not (string= (allout-current-bullet) allout-file-xref-bullet))
635ca796 4754 (error "Current heading lacks cross-reference bullet `%s'"
fd5359c6 4755 allout-file-xref-bullet)
0458fa80
EZ
4756 (let ((inhibit-field-text-motion t)
4757 file-name)
2a1408fd
DN
4758 (save-match-data
4759 (save-excursion
4760 (let* ((text-start allout-recent-prefix-end)
9b026d9f 4761 (heading-end (point-at-eol)))
2a1408fd
DN
4762 (goto-char text-start)
4763 (setq file-name
4764 (if (re-search-forward "\\s-\\(\\S-*\\)" heading-end t)
4765 (buffer-substring (match-beginning 1)
4766 (match-end 1)))))))
69027dea 4767 (setq file-name (expand-file-name file-name))
1977b8f6
RS
4768 (if (or (file-exists-p file-name)
4769 (if (file-writable-p file-name)
4770 (y-or-n-p (format "%s not there, create one? "
4771 file-name))
4772 (error "%s not found and can't be created" file-name)))
4773 (condition-case failure
4774 (find-file-other-window file-name)
d8350998 4775 (error failure))
1977b8f6
RS
4776 (error "%s not found" file-name))
4777 )
4778 )
4779 )
4780 )
19b84ba3 4781
9179616f 4782;;;_ #6 Exposure Control
c567ac01
RS
4783
4784;;;_ - Fundamental
d82979ea
EZ
4785;;;_ > allout-flag-region (from to flag)
4786(defun allout-flag-region (from to flag)
48bd8440 4787 "Conceal text between FROM and TO if FLAG is non-nil, else reveal it.
7b97c764
CY
4788After the exposure changes are made, run the abnormal hook
4789`allout-exposure-change-functions' with the same arguments as
4790this function."
96b83743 4791
96b83743 4792 ;; We use outline invisibility spec.
01fc9422 4793 (remove-overlays from to 'category 'allout-exposure-category)
96b83743 4794 (when flag
2a1408fd 4795 (let ((o (make-overlay from to nil 'front-advance)))
01fc9422 4796 (overlay-put o 'category 'allout-exposure-category)
68ba37fb 4797 (overlay-put o 'evaporate t)
96b83743 4798 (when (featurep 'xemacs)
01fc9422 4799 (let ((props (symbol-plist 'allout-exposure-category)))
96b83743 4800 (while props
1c9b9df0
KM
4801 (condition-case nil
4802 ;; as of 2008-02-27, xemacs lacks modification-hooks
4803 (overlay-put o (pop props) (pop props))
3f2b07f8
KM
4804 (error nil))))))
4805 (setq allout-this-command-hid-text t))
7b97c764 4806 (run-hook-with-args 'allout-exposure-change-functions from to flag))
fd5359c6
MR
4807;;;_ > allout-flag-current-subtree (flag)
4808(defun allout-flag-current-subtree (flag)
96b83743 4809 "Conceal currently-visible topic's subtree if FLAG non-nil, else reveal it."
19b84ba3 4810
c567ac01 4811 (save-excursion
fd5359c6 4812 (allout-back-to-current-heading)
0458fa80
EZ
4813 (let ((inhibit-field-text-motion t))
4814 (end-of-line))
96b83743
EZ
4815 (allout-flag-region (point)
4816 ;; Exposing must not leave trailing blanks hidden,
4817 ;; but can leave them exposed when hiding, so we
4818 ;; can use flag's inverse as the
4819 ;; include-trailing-blank cue:
4820 (allout-end-of-current-subtree (not flag))
4821 flag)))
c567ac01 4822
c567ac01 4823;;;_ - Topic-specific
01fc9422
EZ
4824;;;_ > allout-show-entry ()
4825(defun allout-show-entry ()
4826 "Like `allout-show-current-entry', but reveals entries in hidden topics.
c567ac01
RS
4827
4828This is a way to give restricted peek at a concealed locality without the
4829expense of exposing its context, but can leave the outline with aberrant
96b83743
EZ
4830exposure. `allout-show-offshoot' should be used after the peek to rectify
4831the exposure."
c567ac01
RS
4832
4833 (interactive)
4834 (save-excursion
96b83743 4835 (let (beg end)
d8350998 4836 (allout-goto-prefix-doublechecked)
96b83743
EZ
4837 (setq beg (if (allout-hidden-p) (1- (point)) (point)))
4838 (setq end (allout-pre-next-prefix))
4839 (allout-flag-region beg end nil)
9179616f 4840 (list beg end))))
fd5359c6
MR
4841;;;_ > allout-show-children (&optional level strict)
4842(defun allout-show-children (&optional level strict)
c567ac01
RS
4843
4844 "If point is visible, show all direct subheadings of this heading.
19b84ba3 4845
2265e017 4846Otherwise, do `allout-show-to-offshoot', and then show subheadings.
c567ac01
RS
4847
4848Optional LEVEL specifies how many levels below the current level
4849should be shown, or all levels if t. Default is 1.
4850
4851Optional STRICT means don't resort to -show-to-offshoot, no matter
4852what. This is basically so -show-to-offshoot, which is called by
4853this function, can employ the pure offspring-revealing capabilities of
19b84ba3
RS
4854it.
4855
4856Returns point at end of subtree that was opened, if any. (May get a
4857point of non-opened subtree?)"
c567ac01
RS
4858
4859 (interactive "p")
96b83743 4860 (let ((start-point (point)))
19b84ba3 4861 (if (and (not strict)
96b83743 4862 (allout-hidden-p))
c567ac01 4863
96b83743
EZ
4864 (progn (allout-show-to-offshoot) ; Point's concealed, open to
4865 ; expose it.
4866 ;; Then recurse, but with "strict" set so we don't
4867 ;; infinite regress:
4868 (allout-show-children level t))
c567ac01 4869
19b84ba3 4870 (save-excursion
96b83743
EZ
4871 (allout-beginning-of-current-line)
4872 (save-restriction
d8350998 4873 (let* (depth
65e7eb3d
EZ
4874 ;; translate the level spec for this routine to the ones
4875 ;; used by -chart-subtree and -chart-to-reveal:
4876 (chart-level (cond ((not level) 1)
4877 ((eq level t) nil)
4878 (t level)))
4879 (chart (allout-chart-subtree chart-level))
4880 (to-reveal (or (allout-chart-to-reveal chart chart-level)
d8350998
CY
4881 ;; interactive, show discontinuous children:
4882 (and chart
1c9b9df0 4883 (allout-called-interactively-p)
d8350998
CY
4884 (save-excursion
4885 (allout-back-to-current-heading)
4886 (setq depth (allout-current-depth))
4887 (and (allout-next-heading)
4888 (> allout-recent-depth
4889 (1+ depth))))
4890 (message
4891 "Discontinuous offspring; use `%s %s'%s."
4892 (substitute-command-keys
4893 "\\[universal-argument]")
4894 (substitute-command-keys
4895 "\\[allout-shift-out]")
4896 " to elevate them.")
4897 (allout-chart-to-reveal
4898 chart (- allout-recent-depth depth))))))
96b83743
EZ
4899 (goto-char start-point)
4900 (when (and strict (allout-hidden-p))
4901 ;; Concealed root would already have been taken care of,
4902 ;; unless strict was set.
4903 (allout-flag-region (point) (allout-snug-back) nil)
4904 (when allout-show-bodies
4905 (goto-char (car to-reveal))
4906 (allout-show-current-entry)))
4907 (while to-reveal
4908 (goto-char (car to-reveal))
4909 (allout-flag-region (save-excursion (allout-snug-back) (point))
4910 (progn (search-forward "\n" nil t)
4911 (1- (point)))
4912 nil)
4913 (when allout-show-bodies
4914 (goto-char (car to-reveal))
4915 (allout-show-current-entry))
4916 (setq to-reveal (cdr to-reveal)))))))
4917 ;; Compensate for `save-excursion's maintenance of point
4918 ;; within invisible text:
4919 (goto-char start-point)))
fd5359c6
MR
4920;;;_ > allout-show-to-offshoot ()
4921(defun allout-show-to-offshoot ()
2265e017 4922 "Like `allout-show-entry', but reveals all concealed ancestors, as well.
c567ac01 4923
96b83743 4924Useful for coherently exposing to a random point in a hidden region."
c567ac01
RS
4925 (interactive)
4926 (save-excursion
0458fa80
EZ
4927 (let ((inhibit-field-text-motion t)
4928 (orig-pt (point))
d8350998 4929 (orig-pref (allout-goto-prefix-doublechecked))
c567ac01 4930 (last-at (point))
65e7eb3d
EZ
4931 (bag-it 0))
4932 (while (or (> bag-it 1) (allout-hidden-p))
96b83743 4933 (while (allout-hidden-p)
d8350998 4934 (move-beginning-of-line 1)
96b83743 4935 (if (allout-hidden-p) (forward-char -1)))
c567ac01 4936 (if (= last-at (setq last-at (point)))
65e7eb3d
EZ
4937 ;; Oops, we're not making any progress! Show the current topic
4938 ;; completely, and try one more time here, if we haven't already.
c567ac01 4939 (progn (beginning-of-line)
fd5359c6 4940 (allout-show-current-subtree)
c567ac01 4941 (goto-char orig-pt)
65e7eb3d
EZ
4942 (setq bag-it (1+ bag-it))
4943 (if (> bag-it 1)
4944 (error "allout-show-to-offshoot: %s"
4945 "Stumped by aberrant nesting.")))
4946 (if (> bag-it 0) (setq bag-it 0))
d8350998
CY
4947 (allout-show-children)
4948 (goto-char orig-pref)))
19b84ba3 4949 (goto-char orig-pt)))
fd5359c6
MR
4950 (if (allout-hidden-p)
4951 (allout-show-entry)))
4952;;;_ > allout-hide-current-entry ()
4953(defun allout-hide-current-entry ()
c567ac01
RS
4954 "Hide the body directly following this heading."
4955 (interactive)
fd5359c6 4956 (allout-back-to-current-heading)
c567ac01 4957 (save-excursion
0458fa80
EZ
4958 (let ((inhibit-field-text-motion t))
4959 (end-of-line))
96b83743 4960 (allout-flag-region (point)
d82979ea 4961 (progn (allout-end-of-entry) (point))
96b83743 4962 t)))
fd5359c6
MR
4963;;;_ > allout-show-current-entry (&optional arg)
4964(defun allout-show-current-entry (&optional arg)
96b83743 4965 "Show body following current heading, or hide entry with universal argument."
c567ac01
RS
4966
4967 (interactive "P")
4968 (if arg
fd5359c6 4969 (allout-hide-current-entry)
96b83743 4970 (save-excursion (allout-show-to-offshoot))
c567ac01 4971 (save-excursion
fd5359c6 4972 (allout-flag-region (point)
96b83743
EZ
4973 (progn (allout-end-of-entry t) (point))
4974 nil)
d82979ea 4975 )))
fd5359c6
MR
4976;;;_ > allout-show-current-subtree (&optional arg)
4977(defun allout-show-current-subtree (&optional arg)
75bcb912
JB
4978 "Show everything within the current topic.
4979With a repeat-count, expose this topic and its siblings."
19b84ba3
RS
4980 (interactive "P")
4981 (save-excursion
fd5359c6 4982 (if (<= (allout-current-depth) 0)
60c7bbd2 4983 ;; Outside any topics -- try to get to the first:
fd5359c6 4984 (if (not (allout-next-heading))
6a05d05f 4985 (error "No topics")
60c7bbd2
RS
4986 ;; got to first, outermost topic -- set to expose it and siblings:
4987 (message "Above outermost topic -- exposing all.")
96b83743
EZ
4988 (allout-flag-region (point-min)(point-max) nil))
4989 (allout-beginning-of-current-line)
19b84ba3 4990 (if (not arg)
96b83743 4991 (allout-flag-current-subtree nil)
fd5359c6
MR
4992 (allout-beginning-of-level)
4993 (allout-expose-topic '(* :))))))
96b83743
EZ
4994;;;_ > allout-current-topic-collapsed-p (&optional include-single-liners)
4995(defun allout-current-topic-collapsed-p (&optional include-single-liners)
4996 "True if the currently visible containing topic is already collapsed.
4997
64686e6d
RS
4998Single line topics intrinsically can be considered as being both
4999collapsed and uncollapsed. If optional INCLUDE-SINGLE-LINERS is
5000true, then single-line topics are considered to be collapsed. By
5001default, they are treated as being uncollapsed."
2a1408fd
DN
5002 (save-match-data
5003 (save-excursion
5004 (and
5005 ;; Is the topic all on one line (allowing for trailing blank line)?
5006 (>= (progn (allout-back-to-current-heading)
82b392a4
KM
5007 (let ((inhibit-field-text-motion t))
5008 (move-end-of-line 1))
2a1408fd
DN
5009 (point))
5010 (allout-end-of-current-subtree (not (looking-at "\n\n"))))
5011
5012 (or include-single-liners
5013 (progn (backward-char 1) (allout-hidden-p)))))))
fd5359c6
MR
5014;;;_ > allout-hide-current-subtree (&optional just-close)
5015(defun allout-hide-current-subtree (&optional just-close)
19b84ba3 5016 "Close the current topic, or containing topic if this one is already closed.
c567ac01 5017
19b84ba3 5018If this topic is closed and it's a top level topic, close this topic
353e2ef2 5019and its siblings.
19b84ba3 5020
96b83743 5021If optional arg JUST-CLOSE is non-nil, do not close the parent or
19b84ba3 5022siblings, even if the target topic is already closed."
c567ac01
RS
5023
5024 (interactive)
96b83743 5025 (let* ((from (point))
60c7bbd2 5026 (sibs-msg "Top-level topic already closed -- closing siblings...")
96b83743
EZ
5027 (current-exposed (not (allout-current-topic-collapsed-p t))))
5028 (cond (current-exposed (allout-flag-current-subtree t))
5029 (just-close nil)
d8350998 5030 ((allout-ascend) (allout-hide-current-subtree))
96b83743
EZ
5031 (t (goto-char 0)
5032 (message sibs-msg)
d8350998 5033 (allout-goto-prefix-doublechecked)
96b83743
EZ
5034 (allout-expose-topic '(0 :))
5035 (message (concat sibs-msg " Done."))))
5036 (goto-char from)))
2a1408fd
DN
5037;;;_ > allout-toggle-current-subtree-exposure
5038(defun allout-toggle-current-subtree-exposure ()
5039 "Show or hide the current subtree depending on its current state."
5040 ;; thanks to tassilo for suggesting this.
5041 (interactive)
5042 (save-excursion
5043 (allout-back-to-heading)
5044 (if (allout-hidden-p (point-at-eol))
5045 (allout-show-current-subtree)
5046 (allout-hide-current-subtree))))
fd5359c6
MR
5047;;;_ > allout-show-current-branches ()
5048(defun allout-show-current-branches ()
c567ac01
RS
5049 "Show all subheadings of this heading, but not their bodies."
5050 (interactive)
0458fa80
EZ
5051 (let ((inhibit-field-text-motion t))
5052 (beginning-of-line))
fd5359c6
MR
5053 (allout-show-children t))
5054;;;_ > allout-hide-current-leaves ()
5055(defun allout-hide-current-leaves ()
353e2ef2 5056 "Hide the bodies of the current topic and all its offspring."
c567ac01 5057 (interactive)
fd5359c6
MR
5058 (allout-back-to-current-heading)
5059 (allout-hide-region-body (point) (progn (allout-end-of-current-subtree)
c567ac01
RS
5060 (point))))
5061
5062;;;_ - Region and beyond
fd5359c6
MR
5063;;;_ > allout-show-all ()
5064(defun allout-show-all ()
c567ac01
RS
5065 "Show all of the text in the buffer."
5066 (interactive)
19b84ba3 5067 (message "Exposing entire buffer...")
96b83743 5068 (allout-flag-region (point-min) (point-max) nil)
19b84ba3 5069 (message "Exposing entire buffer... Done."))
fd5359c6
MR
5070;;;_ > allout-hide-bodies ()
5071(defun allout-hide-bodies ()
c567ac01
RS
5072 "Hide all of buffer except headings."
5073 (interactive)
fd5359c6
MR
5074 (allout-hide-region-body (point-min) (point-max)))
5075;;;_ > allout-hide-region-body (start end)
5076(defun allout-hide-region-body (start end)
c567ac01 5077 "Hide all body lines in the region, but not headings."
2a1408fd
DN
5078 (save-match-data
5079 (save-excursion
5080 (save-restriction
5081 (narrow-to-region start end)
5082 (goto-char (point-min))
5083 (let ((inhibit-field-text-motion t))
5084 (while (not (eobp))
5085 (end-of-line)
5086 (allout-flag-region (point) (allout-end-of-entry) t)
5087 (if (not (eobp))
5088 (forward-char
5089 (if (looking-at "\n\n")
5090 2 1)))))))))
c567ac01 5091
fd5359c6
MR
5092;;;_ > allout-expose-topic (spec)
5093(defun allout-expose-topic (spec)
19b84ba3 5094 "Apply exposure specs to successive outline topic items.
c567ac01 5095
fd5359c6
MR
5096Use the more convenient frontend, `allout-new-exposure', if you don't
5097need evaluation of the arguments, or even better, the `allout-layout'
19b84ba3
RS
5098variable-keyed mode-activation/auto-exposure feature of allout outline
5099mode. See the respective documentation strings for more details.
c567ac01
RS
5100
5101Cursor is left at start position.
5102
19b84ba3
RS
5103SPEC is either a number or a list.
5104
5105Successive specs on a list are applied to successive sibling topics.
c567ac01 5106
c07583cd 5107A simple spec (either a number, one of a few symbols, or the null
19b84ba3
RS
5108list) dictates the exposure for the corresponding topic.
5109
5110Non-null lists recursively designate exposure specs for respective
5111subtopics of the current topic.
c567ac01 5112
353e2ef2 5113The `:' repeat spec is used to specify exposure for any number of
19b84ba3 5114successive siblings, up to the trailing ones for which there are
353e2ef2 5115explicit specs following the `:'.
c567ac01
RS
5116
5117Simple (numeric and null-list) specs are interpreted as follows:
5118
19b84ba3
RS
5119 Numbers indicate the relative depth to open the corresponding topic.
5120 - negative numbers force the topic to be closed before opening to the
5121 absolute value of the number, so all siblings are open only to
5122 that level.
5123 - positive numbers open to the relative depth indicated by the
5124 number, but do not force already opened subtopics to be closed.
60c7bbd2 5125 - 0 means to close topic -- hide all offspring.
353e2ef2 5126 : - `repeat'
c567ac01 5127 apply prior element to all siblings at current level, *up to*
353e2ef2 5128 those siblings that would be covered by specs following the `:'
c567ac01 5129 on the list. Ie, apply to all topics at level but the last
c07583cd 5130 ones. (Only first of multiple colons at same level is
60c7bbd2 5131 respected -- subsequent ones are discarded.)
19b84ba3
RS
5132 * - completely opens the topic, including bodies.
5133 + - shows all the sub headers, but not the bodies
5134 - - exposes the body of the corresponding topic.
c567ac01
RS
5135
5136Examples:
fd5359c6 5137\(allout-expose-topic '(-1 : 0))
c567ac01
RS
5138 Close this and all following topics at current level, exposing
5139 only their immediate children, but close down the last topic
5140 at this current level completely.
fd5359c6 5141\(allout-expose-topic '(-1 () : 1 0))
c567ac01
RS
5142 Close current topic so only the immediate subtopics are shown;
5143 show the children in the second to last topic, and completely
5144 close the last one.
fd5359c6 5145\(allout-expose-topic '(-2 : -1 *))
c567ac01
RS
5146 Expose children and grandchildren of all topics at current
5147 level except the last two; expose children of the second to
5148 last and completely open the last one."
5149
5150 (interactive "xExposure spec: ")
19b84ba3
RS
5151 (if (not (listp spec))
5152 nil
fd5359c6 5153 (let ((depth (allout-depth))
19b84ba3
RS
5154 (max-pos 0)
5155 prev-elem curr-elem
96b83743 5156 stay)
19b84ba3
RS
5157 (while spec
5158 (setq prev-elem curr-elem
5159 curr-elem (car spec)
5160 spec (cdr spec))
5161 (cond ; Do current element:
5162 ((null curr-elem) nil)
5163 ((symbolp curr-elem)
fd5359c6
MR
5164 (cond ((eq curr-elem '*) (allout-show-current-subtree)
5165 (if (> allout-recent-end-of-subtree max-pos)
5166 (setq max-pos allout-recent-end-of-subtree)))
65e7eb3d
EZ
5167 ((eq curr-elem '+)
5168 (if (not (allout-hidden-p))
5169 (save-excursion (allout-hide-current-subtree t)))
5170 (allout-show-current-branches)
fd5359c6
MR
5171 (if (> allout-recent-end-of-subtree max-pos)
5172 (setq max-pos allout-recent-end-of-subtree)))
5173 ((eq curr-elem '-) (allout-show-current-entry))
19b84ba3
RS
5174 ((eq curr-elem ':)
5175 (setq stay t)
353e2ef2 5176 ;; Expand the `repeat' spec to an explicit version,
19b84ba3
RS
5177 ;; w.r.t. remaining siblings:
5178 (let ((residue ; = # of sibs not covered by remaining spec
60c7bbd2 5179 ;; Dang, could be nice to make use of the chart, sigh:
fd5359c6 5180 (- (length (allout-chart-siblings))
19b84ba3
RS
5181 (length spec))))
5182 (if (< 0 residue)
60c7bbd2 5183 ;; Some residue -- cover it with prev-elem:
19b84ba3
RS
5184 (setq spec (append (make-list residue prev-elem)
5185 spec)))))))
5186 ((numberp curr-elem)
96b83743 5187 (if (and (>= 0 curr-elem) (not (allout-hidden-p)))
fd5359c6 5188 (save-excursion (allout-hide-current-subtree t)
19b84ba3
RS
5189 (if (> 0 curr-elem)
5190 nil
fd5359c6 5191 (if (> allout-recent-end-of-subtree max-pos)
19b84ba3 5192 (setq max-pos
fd5359c6 5193 allout-recent-end-of-subtree)))))
19b84ba3 5194 (if (> (abs curr-elem) 0)
fd5359c6
MR
5195 (progn (allout-show-children (abs curr-elem))
5196 (if (> allout-recent-end-of-subtree max-pos)
5197 (setq max-pos allout-recent-end-of-subtree)))))
19b84ba3 5198 ((listp curr-elem)
fd5359c6
MR
5199 (if (allout-descend-to-depth (1+ depth))
5200 (let ((got (allout-expose-topic curr-elem)))
19b84ba3
RS
5201 (if (and got (> got max-pos)) (setq max-pos got))))))
5202 (cond (stay (setq stay nil))
5203 ((listp (car spec)) nil)
5204 ((> max-pos (point))
5205 ;; Capitalize on max-pos state to get us nearer next sibling:
5206 (progn (goto-char (min (point-max) max-pos))
fd5359c6
MR
5207 (allout-next-heading)))
5208 ((allout-next-sibling depth))))
19b84ba3 5209 max-pos)))
fd5359c6
MR
5210;;;_ > allout-old-expose-topic (spec &rest followers)
5211(defun allout-old-expose-topic (spec &rest followers)
d82979ea 5212
c07583cd 5213 "Deprecated. Use `allout-expose-topic' (with different schema
d82979ea
EZ
5214format) instead.
5215
5216Dictate wholesale exposure scheme for current topic, according to SPEC.
c567ac01
RS
5217
5218SPEC is either a number or a list. Optional successive args
5219dictate exposure for subsequent siblings of current topic.
5220
5221A simple spec (either a number, a special symbol, or the null list)
5222dictates the overall exposure for a topic. Non null lists are
5223composite specs whose first element dictates the overall exposure for
5224a topic, with the subsequent elements in the list interpreted as specs
5225that dictate the exposure for the successive offspring of the topic.
5226
5227Simple (numeric and null-list) specs are interpreted as follows:
5228
5229 - Numbers indicate the relative depth to open the corresponding topic:
5230 - negative numbers force the topic to be close before opening to the
5231 absolute value of the number.
5232 - positive numbers just open to the relative depth indicated by the number.
5233 - 0 just closes
353e2ef2
KH
5234 - `*' completely opens the topic, including bodies.
5235 - `+' shows all the sub headers, but not the bodies
5236 - `-' exposes the body and immediate offspring of the corresponding topic.
c567ac01
RS
5237
5238If the spec is a list, the first element must be a number, which
5239dictates the exposure depth of the topic as a whole. Subsequent
5240elements of the list are nested SPECs, dictating the specific exposure
5241for the corresponding offspring of the topic.
5242
e126900f 5243Optional FOLLOWERS arguments dictate exposure for succeeding siblings."
c567ac01
RS
5244
5245 (interactive "xExposure spec: ")
0458fa80
EZ
5246 (let ((inhibit-field-text-motion t)
5247 (depth (allout-current-depth))
c567ac01
RS
5248 max-pos)
5249 (cond ((null spec) nil)
5250 ((symbolp spec)
fd5359c6
MR
5251 (if (eq spec '*) (allout-show-current-subtree))
5252 (if (eq spec '+) (allout-show-current-branches))
5253 (if (eq spec '-) (allout-show-current-entry)))
c567ac01
RS
5254 ((numberp spec)
5255 (if (>= 0 spec)
fd5359c6 5256 (save-excursion (allout-hide-current-subtree t)
c567ac01
RS
5257 (end-of-line)
5258 (if (or (not max-pos)
5259 (> (point) max-pos))
5260 (setq max-pos (point)))
5261 (if (> 0 spec)
5262 (setq spec (* -1 spec)))))
5263 (if (> spec 0)
fd5359c6 5264 (allout-show-children spec)))
c567ac01 5265 ((listp spec)
fd5359c6 5266 ;(let ((got (allout-old-expose-topic (car spec))))
c567ac01
RS
5267 ; (if (and got (or (not max-pos) (> got max-pos)))
5268 ; (setq max-pos got)))
fd5359c6 5269 (let ((new-depth (+ (allout-current-depth) 1))
c567ac01 5270 got)
fd5359c6 5271 (setq max-pos (allout-old-expose-topic (car spec)))
c567ac01
RS
5272 (setq spec (cdr spec))
5273 (if (and spec
fd5359c6
MR
5274 (allout-descend-to-depth new-depth)
5275 (not (allout-hidden-p)))
5276 (progn (setq got (apply 'allout-old-expose-topic spec))
c567ac01
RS
5277 (if (and got (or (not max-pos) (> got max-pos)))
5278 (setq max-pos got)))))))
5279 (while (and followers
5280 (progn (if (and max-pos (< (point) max-pos))
5281 (progn (goto-char max-pos)
5282 (setq max-pos nil)))
5283 (end-of-line)
fd5359c6
MR
5284 (allout-next-sibling depth)))
5285 (allout-old-expose-topic (car followers))
c567ac01
RS
5286 (setq followers (cdr followers)))
5287 max-pos))
fd5359c6
MR
5288;;;_ > allout-new-exposure '()
5289(defmacro allout-new-exposure (&rest spec)
5290 "Literal frontend for `allout-expose-topic', doesn't evaluate arguments.
2265e017
MR
5291Some arguments that would need to be quoted in `allout-expose-topic'
5292need not be quoted in `allout-new-exposure'.
c567ac01
RS
5293
5294Cursor is left at start position.
5295
d82979ea
EZ
5296Use this instead of obsolete `allout-exposure'.
5297
c567ac01 5298Examples:
bff05d9e 5299\(allout-new-exposure (-1 () () () 1) 0)
c567ac01
RS
5300 Close current topic at current level so only the immediate
5301 subtopics are shown, except also show the children of the
5302 third subtopic; and close the next topic at the current level.
bff05d9e 5303\(allout-new-exposure : -1 0)
c567ac01
RS
5304 Close all topics at current level to expose only their
5305 immediate children, except for the last topic at the current
353e2ef2 5306 level, in which even its immediate children are hidden.
bff05d9e 5307\(allout-new-exposure -2 : -1 *)
c567ac01
RS
5308 Expose children and grandchildren of first topic at current
5309 level, and expose children of subsequent topics at current
5310 level *except* for the last, which should be opened completely."
651eaf36
AS
5311 `(save-excursion
5312 (if (not (or (allout-goto-prefix-doublechecked)
5313 (allout-next-heading)))
5314 (error "allout-new-exposure: Can't find any outline topics"))
5315 (allout-expose-topic ',spec)))
c567ac01 5316
60c7bbd2 5317;;;_ #7 Systematic outline presentation -- copying, printing, flattening
19b84ba3 5318
9179616f
DL
5319;;;_ - Mapping and processing of topics
5320;;;_ ( See also Subtree Charting, in Navigation code.)
fd5359c6
MR
5321;;;_ > allout-stringify-flat-index (flat-index)
5322(defun allout-stringify-flat-index (flat-index &optional context)
9179616f
DL
5323 "Convert list representing section/subsection/... to document string.
5324
5325Optional arg CONTEXT indicates interior levels to include."
5326 (let ((delim ".")
71296446 5327 result
9179616f
DL
5328 numstr
5329 (context-depth (or (and context 2) 1)))
5330 ;; Take care of the explicit context:
5331 (while (> context-depth 0)
5332 (setq numstr (int-to-string (car flat-index))
5333 flat-index (cdr flat-index)
5334 result (if flat-index
5335 (cons delim (cons numstr result))
5336 (cons numstr result))
5337 context-depth (if flat-index (1- context-depth) 0)))
5338 (setq delim " ")
5339 ;; Take care of the indentation:
5340 (if flat-index
5341 (progn
5342 (while flat-index
5343 (setq result
5344 (cons delim
5345 (cons (make-string
5346 (1+ (truncate (if (zerop (car flat-index))
5347 1
89561f72 5348 (log (car flat-index) 10))))
9179616f
DL
5349 ? )
5350 result)))
5351 (setq flat-index (cdr flat-index)))
5352 ;; Dispose of single extra delim:
5353 (setq result (cdr result))))
5354 (apply 'concat result)))
fd5359c6
MR
5355;;;_ > allout-stringify-flat-index-plain (flat-index)
5356(defun allout-stringify-flat-index-plain (flat-index)
9179616f
DL
5357 "Convert list representing section/subsection/... to document string."
5358 (let ((delim ".")
5359 result)
5360 (while flat-index
5361 (setq result (cons (int-to-string (car flat-index))
5362 (if result
5363 (cons delim result))))
5364 (setq flat-index (cdr flat-index)))
5365 (apply 'concat result)))
fd5359c6
MR
5366;;;_ > allout-stringify-flat-index-indented (flat-index)
5367(defun allout-stringify-flat-index-indented (flat-index)
9179616f
DL
5368 "Convert list representing section/subsection/... to document string."
5369 (let ((delim ".")
71296446 5370 result
9179616f
DL
5371 numstr)
5372 ;; Take care of the explicit context:
5373 (setq numstr (int-to-string (car flat-index))
5374 flat-index (cdr flat-index)
5375 result (if flat-index
5376 (cons delim (cons numstr result))
5377 (cons numstr result)))
5378 (setq delim " ")
5379 ;; Take care of the indentation:
5380 (if flat-index
5381 (progn
5382 (while flat-index
5383 (setq result
5384 (cons delim
5385 (cons (make-string
5386 (1+ (truncate (if (zerop (car flat-index))
5387 1
89561f72 5388 (log (car flat-index) 10))))
9179616f
DL
5389 ? )
5390 result)))
5391 (setq flat-index (cdr flat-index)))
5392 ;; Dispose of single extra delim:
5393 (setq result (cdr result))))
5394 (apply 'concat result)))
fd5359c6
MR
5395;;;_ > allout-listify-exposed (&optional start end format)
5396(defun allout-listify-exposed (&optional start end format)
19b84ba3 5397
9179616f 5398 "Produce a list representing exposed topics in current region.
c567ac01 5399
fd5359c6 5400This list can then be used by `allout-process-exposed' to manipulate
9179616f 5401the subject region.
353e2ef2 5402
9179616f 5403Optional START and END indicate bounds of region.
19b84ba3 5404
75bcb912 5405Optional arg, FORMAT, designates an alternate presentation form for
9179616f 5406the prefix:
c567ac01 5407
60c7bbd2 5408 list -- Present prefix as numeric section.subsection..., starting with
9179616f 5409 section indicated by the list, innermost nesting first.
60c7bbd2 5410 `indent' (symbol) -- Convert header prefixes to all white space,
9179616f 5411 except for distinctive bullets.
c567ac01 5412
9179616f
DL
5413The elements of the list produced are lists that represents a topic
5414header and body. The elements of that list are:
c567ac01 5415
9179616f
DL
5416 - a number representing the depth of the topic,
5417 - a string representing the header-prefix, including trailing whitespace and
5418 bullet.
5419 - a string representing the bullet character,
5420 - and a series of strings, each containing one line of the exposed
5421 portion of the topic entry."
c567ac01 5422
9179616f
DL
5423 (interactive "r")
5424 (save-excursion
5425 (let*
0458fa80
EZ
5426 ((inhibit-field-text-motion t)
5427 ;; state vars:
5428 strings prefix result depth new-depth out gone-out bullet beg
9179616f 5429 next done)
c567ac01 5430
9179616f
DL
5431 (goto-char start)
5432 (beginning-of-line)
97610156 5433 ;; Goto initial topic, and register preceding stuff, if any:
d8350998 5434 (if (> (allout-goto-prefix-doublechecked) start)
60c7bbd2 5435 ;; First topic follows beginning point -- register preliminary stuff:
3e2ee889
KM
5436 (setq result
5437 (list (list 0 "" nil
5438 (buffer-substring-no-properties start
5439 (1- (point)))))))
9179616f
DL
5440 (while (and (not done)
5441 (not (eobp)) ; Loop until we've covered the region.
5442 (not (> (point) end)))
d8350998 5443 (setq depth allout-recent-depth ; Current topics depth,
fd5359c6
MR
5444 bullet (allout-recent-bullet) ; ... bullet,
5445 prefix (allout-recent-prefix)
5446 beg (progn (allout-end-of-prefix t) (point))) ; and beginning.
9179616f 5447 (setq done ; The boundary for the current topic:
fd5359c6 5448 (not (allout-next-visible-heading 1)))
d8350998 5449 (setq new-depth allout-recent-depth)
9179616f
DL
5450 (setq gone-out out
5451 out (< new-depth depth))
5452 (beginning-of-line)
5453 (setq next (point))
5454 (goto-char beg)
5455 (setq strings nil)
5456 (while (> next (point)) ; Get all the exposed text in
5457 (setq strings
3e2ee889 5458 (cons (buffer-substring-no-properties
9179616f
DL
5459 beg
5460 ;To hidden text or end of line:
5461 (progn
96b83743
EZ
5462 (end-of-line)
5463 (allout-back-to-visible-text)))
9179616f 5464 strings))
96b83743 5465 (when (< (point) next) ; Resume from after hid text, if any.
1b4d97b2
CY
5466 (line-move 1)
5467 (beginning-of-line))
9179616f
DL
5468 (setq beg (point)))
5469 ;; Accumulate list for this topic:
5470 (setq strings (nreverse strings))
5471 (setq result
5472 (cons
5473 (if format
5474 (let ((special (if (string-match
5475 (regexp-quote bullet)
fd5359c6 5476 allout-distinctive-bullets-string)
9179616f
DL
5477 bullet)))
5478 (cond ((listp format)
5479 (list depth
96497653 5480 (if allout-flattened-numbering-abbreviation
fd5359c6 5481 (allout-stringify-flat-index format
9179616f 5482 gone-out)
fd5359c6 5483 (allout-stringify-flat-index-plain
9179616f
DL
5484 format))
5485 strings
5486 special))
5487 ((eq format 'indent)
5488 (if special
5489 (list depth
5490 (concat (make-string (1+ depth) ? )
5491 (substring prefix -1))
5492 strings)
5493 (list depth
5494 (make-string depth ? )
5495 strings)))
fd5359c6 5496 (t (error "allout-listify-exposed: %s %s"
9179616f
DL
5497 "invalid format" format))))
5498 (list depth prefix strings))
5499 result))
09e80d9f 5500 ;; Reassess format, if any:
9179616f
DL
5501 (if (and format (listp format))
5502 (cond ((= new-depth depth)
5503 (setq format (cons (1+ (car format))
5504 (cdr format))))
60c7bbd2 5505 ((> new-depth depth) ; descending -- assume by 1:
9179616f
DL
5506 (setq format (cons 1 format)))
5507 (t
5508 ; Pop the residue:
5509 (while (< new-depth depth)
5510 (setq format (cdr format))
5511 (setq depth (1- depth)))
5512 ; And increment the current one:
5513 (setq format
5514 (cons (1+ (or (car format)
5515 -1))
5516 (cdr format)))))))
5517 ;; Put the list with first at front, to last at back:
5518 (nreverse result))))
c60d543d
RS
5519;;;_ > allout-region-active-p ()
5520(defmacro allout-region-active-p ()
42c3e51c
KM
5521 (cond ((fboundp 'use-region-p) '(use-region-p))
5522 ((fboundp 'region-active-p) '(region-active-p))
5523 (t 'mark-active)))
5524;;_ > allout-process-exposed (&optional func from to frombuf
9179616f 5525;;; tobuf format)
fd5359c6 5526(defun allout-process-exposed (&optional func from to frombuf tobuf
9d3aa82c 5527 format _start-num)
9179616f 5528 "Map function on exposed parts of current topic; results to another buffer.
353e2ef2 5529
d82979ea
EZ
5530All args are options; default values itemized below.
5531
5532Apply FUNCTION to exposed portions FROM position TO position in buffer
9179616f
DL
5533FROMBUF to buffer TOBUF. Sixth optional arg, FORMAT, designates an
5534alternate presentation form:
c567ac01 5535
60c7bbd2 5536 `flat' -- Present prefix as numeric section.subsection..., starting with
75bcb912 5537 section indicated by the START-NUM, innermost nesting first.
60c7bbd2 5538 X`flat-indented' -- Prefix is like `flat' for first topic at each
9179616f
DL
5539 X level, but subsequent topics have only leaf topic
5540 X number, padded with blanks to line up with first.
60c7bbd2 5541 `indent' (symbol) -- Convert header prefixes to all white space,
9179616f 5542 except for distinctive bullets.
c567ac01 5543
9179616f 5544Defaults:
d82979ea 5545 FUNCTION: `allout-insert-listified'
9179616f
DL
5546 FROM: region start, if region active, else start of buffer
5547 TO: region end, if region active, else end of buffer
5548 FROMBUF: current buffer
5549 TOBUF: buffer name derived: \"*current-buffer-name exposed*\"
5550 FORMAT: nil"
5551
5552 ; Resolve arguments,
5553 ; defaulting if necessary:
fd5359c6 5554 (if (not func) (setq func 'allout-insert-listified))
9179616f 5555 (if (not (and from to))
c60d543d 5556 (if (allout-region-active-p)
9179616f
DL
5557 (setq from (region-beginning) to (region-end))
5558 (setq from (point-min) to (point-max))))
5559 (if frombuf
5560 (if (not (bufferp frombuf))
60c7bbd2 5561 ;; Specified but not a buffer -- get it:
9179616f
DL
5562 (let ((got (get-buffer frombuf)))
5563 (if (not got)
fd5359c6 5564 (error (concat "allout-process-exposed: source buffer "
9179616f
DL
5565 frombuf
5566 " not found."))
5567 (setq frombuf got))))
60c7bbd2 5568 ;; not specified -- default it:
9179616f
DL
5569 (setq frombuf (current-buffer)))
5570 (if tobuf
5571 (if (not (bufferp tobuf))
5572 (setq tobuf (get-buffer-create tobuf)))
60c7bbd2 5573 ;; not specified -- default it:
9179616f
DL
5574 (setq tobuf (concat "*" (buffer-name frombuf) " exposed*")))
5575 (if (listp format)
5576 (nreverse format))
5577
5578 (let* ((listified
5579 (progn (set-buffer frombuf)
fd5359c6 5580 (allout-listify-exposed from to format))))
9179616f 5581 (set-buffer tobuf)
5110e166 5582 (mapc func listified)
9179616f
DL
5583 (pop-to-buffer tobuf)))
5584
5585;;;_ - Copy exposed
fd5359c6
MR
5586;;;_ > allout-insert-listified (listified)
5587(defun allout-insert-listified (listified)
9179616f
DL
5588 "Insert contents of listified outline portion in current buffer.
5589
e126900f 5590LISTIFIED is a list representing each topic header and body:
9179616f
DL
5591
5592 \`(depth prefix text)'
5593
d82979ea 5594or \`(depth prefix text bullet-plus)'
9179616f
DL
5595
5596If `bullet-plus' is specified, it is inserted just after the entire prefix."
5597 (setq listified (cdr listified))
5598 (let ((prefix (prog1
5599 (car listified)
5600 (setq listified (cdr listified))))
5601 (text (prog1
5602 (car listified)
5603 (setq listified (cdr listified))))
5604 (bullet-plus (car listified)))
eac9cf5f
PJ
5605 (insert prefix)
5606 (if bullet-plus (insert (concat " " bullet-plus)))
9179616f 5607 (while text
eac9cf5f 5608 (insert (car text))
9179616f 5609 (if (setq text (cdr text))
0949617b 5610 (insert "\n")))
eac9cf5f 5611 (insert "\n")))
fd5359c6
MR
5612;;;_ > allout-copy-exposed-to-buffer (&optional arg tobuf format)
5613(defun allout-copy-exposed-to-buffer (&optional arg tobuf format)
9179616f
DL
5614 "Duplicate exposed portions of current outline to another buffer.
5615
5616Other buffer has current buffers name with \" exposed\" appended to it.
5617
5618With repeat count, copy the exposed parts of only the current topic.
5619
5620Optional second arg TOBUF is target buffer name.
5621
5622Optional third arg FORMAT, if non-nil, symbolically designates an
5623alternate presentation format for the outline:
5624
5625 `flat' - Convert topic header prefixes to numeric
5626 section.subsection... identifiers.
5627 `indent' - Convert header prefixes to all white space, except for
5628 distinctive bullets.
5629 `indent-flat' - The best of both - only the first of each level has
5630 the full path, the rest have only the section number
5631 of the leaf, preceded by the right amount of indentation."
c567ac01
RS
5632
5633 (interactive "P")
5634 (if (not tobuf)
5635 (setq tobuf (get-buffer-create (concat "*" (buffer-name) " exposed*"))))
5636 (let* ((start-pt (point))
fd5359c6
MR
5637 (beg (if arg (allout-back-to-current-heading) (point-min)))
5638 (end (if arg (allout-end-of-current-subtree) (point-max)))
9179616f
DL
5639 (buf (current-buffer))
5640 (start-list ()))
5641 (if (eq format 'flat)
5642 (setq format (if arg (save-excursion
5643 (goto-char beg)
fd5359c6 5644 (allout-topic-flat-index))
9179616f 5645 '(1))))
7fdbcd83 5646 (with-current-buffer tobuf (erase-buffer))
fd5359c6 5647 (allout-process-exposed 'allout-insert-listified
c567ac01
RS
5648 beg
5649 end
5650 (current-buffer)
9179616f
DL
5651 tobuf
5652 format start-list)
c567ac01
RS
5653 (goto-char (point-min))
5654 (pop-to-buffer buf)
5655 (goto-char start-pt)))
fd5359c6
MR
5656;;;_ > allout-flatten-exposed-to-buffer (&optional arg tobuf)
5657(defun allout-flatten-exposed-to-buffer (&optional arg tobuf)
9179616f
DL
5658 "Present numeric outline of outline's exposed portions in another buffer.
5659
60c7bbd2 5660The resulting outline is not compatible with outline mode -- use
fd5359c6 5661`allout-copy-exposed-to-buffer' if you want that.
9179616f 5662
fd5359c6 5663Use `allout-indented-exposed-to-buffer' for indented presentation.
9179616f
DL
5664
5665With repeat count, copy the exposed portions of only current topic.
5666
e126900f 5667Other buffer has current buffer's name with \" exposed\" appended to
9179616f
DL
5668it, unless optional second arg TOBUF is specified, in which case it is
5669used verbatim."
5670 (interactive "P")
fd5359c6
MR
5671 (allout-copy-exposed-to-buffer arg tobuf 'flat))
5672;;;_ > allout-indented-exposed-to-buffer (&optional arg tobuf)
5673(defun allout-indented-exposed-to-buffer (&optional arg tobuf)
9179616f
DL
5674 "Present indented outline of outline's exposed portions in another buffer.
5675
60c7bbd2 5676The resulting outline is not compatible with outline mode -- use
fd5359c6 5677`allout-copy-exposed-to-buffer' if you want that.
9179616f 5678
fd5359c6 5679Use `allout-flatten-exposed-to-buffer' for numeric sectional presentation.
9179616f
DL
5680
5681With repeat count, copy the exposed portions of only current topic.
5682
e126900f 5683Other buffer has current buffer's name with \" exposed\" appended to
9179616f
DL
5684it, unless optional second arg TOBUF is specified, in which case it is
5685used verbatim."
5686 (interactive "P")
fd5359c6 5687 (allout-copy-exposed-to-buffer arg tobuf 'indent))
c567ac01
RS
5688
5689;;;_ - LaTeX formatting
e126900f 5690;;;_ > allout-latex-verb-quote (string &optional flow)
9d3aa82c 5691(defun allout-latex-verb-quote (string &optional _flow)
539d7736 5692 "Return copy of STRING for literal reproduction across LaTeX processing.
c07583cd 5693Expresses the original characters (including carriage returns) of the
539d7736 5694string across LaTeX processing."
9179616f
DL
5695 (mapconcat (function
5696 (lambda (char)
c567ac01
RS
5697 (cond ((memq char '(?\\ ?$ ?% ?# ?& ?{ ?} ?_ ?^ ?- ?*))
5698 (concat "\\char" (number-to-string char) "{}"))
5699 ((= char ?\n) "\\\\")
9179616f 5700 (t (char-to-string char)))))
e126900f 5701 string
c567ac01 5702 ""))
fd5359c6
MR
5703;;;_ > allout-latex-verbatim-quote-curr-line ()
5704(defun allout-latex-verbatim-quote-curr-line ()
c07583cd 5705 "Express line for exact (literal) representation across LaTeX processing.
19b84ba3 5706
c07583cd 5707Adjust line contents so it is unaltered (from the original line)
539d7736 5708across LaTeX processing, within the context of a `verbatim'
c567ac01 5709environment. Leaves point at the end of the line."
0458fa80
EZ
5710 (let ((inhibit-field-text-motion t))
5711 (beginning-of-line)
9d3aa82c 5712 (let (;(beg (point))
626b459f 5713 (end (point-at-eol)))
2a1408fd
DN
5714 (save-match-data
5715 (while (re-search-forward "\\\\"
5716 ;;"\\\\\\|\\{\\|\\}\\|\\_\\|\\$\\|\\\"\\|\\&\\|\\^\\|\\-\\|\\*\\|#"
5717 end ; bounded by end-of-line
5718 1) ; no matches, move to end & return nil
5719 (goto-char (match-beginning 2))
5720 (insert "\\")
5721 (setq end (1+ end))
5722 (goto-char (1+ (match-end 2))))))))
e126900f
JB
5723;;;_ > allout-insert-latex-header (buffer)
5724(defun allout-insert-latex-header (buffer)
539d7736 5725 "Insert initial LaTeX commands at point in BUFFER."
c567ac01
RS
5726 ;; Much of this is being derived from the stuff in appendix of E in
5727 ;; the TeXBook, pg 421.
e126900f 5728 (set-buffer buffer)
c567ac01
RS
5729 (let ((doc-style (format "\n\\documentstyle{%s}\n"
5730 "report"))
fd5359c6 5731 (page-numbering (if allout-number-pages
c567ac01
RS
5732 "\\pagestyle{empty}\n"
5733 ""))
c567ac01 5734 (titlecmd (format "\\newcommand{\\titlecmd}[1]{{%s #1}}\n"
fd5359c6 5735 allout-title-style))
c567ac01 5736 (labelcmd (format "\\newcommand{\\labelcmd}[1]{{%s #1}}\n"
fd5359c6 5737 allout-label-style))
c567ac01 5738 (headlinecmd (format "\\newcommand{\\headlinecmd}[1]{{%s #1}}\n"
fd5359c6 5739 allout-head-line-style))
c567ac01 5740 (bodylinecmd (format "\\newcommand{\\bodylinecmd}[1]{{%s #1}}\n"
fd5359c6 5741 allout-body-line-style))
c567ac01
RS
5742 (setlength (format "%s%s%s%s"
5743 "\\newlength{\\stepsize}\n"
5744 "\\setlength{\\stepsize}{"
fd5359c6 5745 allout-indent
c567ac01
RS
5746 "}\n"))
5747 (oneheadline (format "%s%s%s%s%s%s%s"
5748 "\\newcommand{\\OneHeadLine}[3]{%\n"
5749 "\\noindent%\n"
5750 "\\hspace*{#2\\stepsize}%\n"
5751 "\\labelcmd{#1}\\hspace*{.2cm}"
5752 "\\headlinecmd{#3}\\\\["
fd5359c6 5753 allout-line-skip
c567ac01
RS
5754 "]\n}\n"))
5755 (onebodyline (format "%s%s%s%s%s%s"
5756 "\\newcommand{\\OneBodyLine}[2]{%\n"
5757 "\\noindent%\n"
5758 "\\hspace*{#1\\stepsize}%\n"
5759 "\\bodylinecmd{#2}\\\\["
fd5359c6 5760 allout-line-skip
c567ac01
RS
5761 "]\n}\n"))
5762 (begindoc "\\begin{document}\n\\begin{center}\n")
5763 (title (format "%s%s%s%s"
5764 "\\titlecmd{"
fd5359c6 5765 (allout-latex-verb-quote (if allout-title
96b83743 5766 (condition-case nil
fd5359c6 5767 (eval allout-title)
d8350998 5768 (error "<unnamed buffer>"))
c567ac01
RS
5769 "Unnamed Outline"))
5770 "}\n"
5771 "\\end{center}\n\n"))
5772 (hsize "\\hsize = 7.5 true in\n")
5773 (hoffset "\\hoffset = -1.5 true in\n")
5774 (vspace "\\vspace{.1cm}\n\n"))
5775 (insert (concat doc-style
5776 page-numbering
5777 titlecmd
5778 labelcmd
5779 headlinecmd
5780 bodylinecmd
5781 setlength
5782 oneheadline
5783 onebodyline
5784 begindoc
5785 title
5786 hsize
5787 hoffset
5788 vspace)
5789 )))
e126900f
JB
5790;;;_ > allout-insert-latex-trailer (buffer)
5791(defun allout-insert-latex-trailer (buffer)
539d7736 5792 "Insert concluding LaTeX commands at point in BUFFER."
e126900f 5793 (set-buffer buffer)
c567ac01 5794 (insert "\n\\end{document}\n"))
fd5359c6 5795;;;_ > allout-latexify-one-item (depth prefix bullet text)
9d3aa82c 5796(defun allout-latexify-one-item (depth _prefix bullet text)
19b84ba3
RS
5797 "Insert LaTeX commands for formatting one outline item.
5798
9179616f 5799Args are the topics numeric DEPTH, the header PREFIX lead string, the
19b84ba3 5800BULLET string, and a list of TEXT strings for the body."
c567ac01
RS
5801 (let* ((head-line (if text (car text)))
5802 (body-lines (cdr text))
5803 (curr-line)
5804 body-content bop)
5805 ; Do the head line:
71296446 5806 (insert (concat "\\OneHeadLine{\\verb\1 "
d82979ea
EZ
5807 (allout-latex-verb-quote bullet)
5808 "\1}{"
5809 depth
5810 "}{\\verb\1 "
5811 (if head-line
5812 (allout-latex-verb-quote head-line)
5813 "")
5814 "\1}\n"))
c567ac01
RS
5815 (if (not body-lines)
5816 nil
eac9cf5f
PJ
5817 ;;(insert "\\beginlines\n")
5818 (insert "\\begin{verbatim}\n")
c567ac01
RS
5819 (while body-lines
5820 (setq curr-line (car body-lines))
5821 (if (and (not body-content)
5822 (not (string-match "^\\s-*$" curr-line)))
5823 (setq body-content t))
5824 ; Mangle any occurrences of
5825 ; "\end{verbatim}" in text,
5826 ; it's special:
5827 (if (and body-content
5828 (setq bop (string-match "\\end{verbatim}" curr-line)))
5829 (setq curr-line (concat (substring curr-line 0 bop)
5830 ">"
5831 (substring curr-line bop))))
eac9cf5f
PJ
5832 ;;(insert "|" (car body-lines) "|")
5833 (insert curr-line)
fd5359c6 5834 (allout-latex-verbatim-quote-curr-line)
eac9cf5f 5835 (insert "\n")
c567ac01
RS
5836 (setq body-lines (cdr body-lines)))
5837 (if body-content
5838 (setq body-content nil)
5839 (forward-char -1)
eac9cf5f 5840 (insert "\\ ")
c567ac01 5841 (forward-char 1))
eac9cf5f
PJ
5842 ;;(insert "\\endlines\n")
5843 (insert "\\end{verbatim}\n")
c567ac01 5844 )))
fd5359c6
MR
5845;;;_ > allout-latexify-exposed (arg &optional tobuf)
5846(defun allout-latexify-exposed (arg &optional tobuf)
539d7736 5847 "Format current topics exposed portions to TOBUF for LaTeX processing.
19b84ba3
RS
5848TOBUF defaults to a buffer named the same as the current buffer, but
5849with \"*\" prepended and \" latex-formed*\" appended.
c567ac01
RS
5850
5851With repeat count, copy the exposed portions of entire buffer."
5852
5853 (interactive "P")
5854 (if (not tobuf)
5855 (setq tobuf
5856 (get-buffer-create (concat "*" (buffer-name) " latexified*"))))
5857 (let* ((start-pt (point))
fd5359c6
MR
5858 (beg (if arg (point-min) (allout-back-to-current-heading)))
5859 (end (if arg (point-max) (allout-end-of-current-subtree)))
c567ac01
RS
5860 (buf (current-buffer)))
5861 (set-buffer tobuf)
1977b8f6 5862 (erase-buffer)
fd5359c6 5863 (allout-insert-latex-header tobuf)
c567ac01 5864 (goto-char (point-max))
fd5359c6 5865 (allout-process-exposed 'allout-latexify-one-item
c567ac01
RS
5866 beg
5867 end
5868 buf
5869 tobuf)
5870 (goto-char (point-max))
fd5359c6 5871 (allout-insert-latex-trailer tobuf)
1977b8f6 5872 (goto-char (point-min))
c567ac01
RS
5873 (pop-to-buffer buf)
5874 (goto-char start-pt)))
5875
d82979ea 5876;;;_ #8 Encryption
29fac3fe
KM
5877;;;_ > allout-toggle-current-subtree-encryption (&optional keymode-cue)
5878(defun allout-toggle-current-subtree-encryption (&optional keymode-cue)
5879 "Encrypt clear or decrypt encoded topic text.
5880
20db1522 5881Allout uses Emacs 'epg' library to perform encryption. Symmetric
29fac3fe
KM
5882and keypair encryption are supported. All encryption is ascii
5883armored.
5884
075746e2 5885Entry encryption defaults to symmetric key mode unless keypair
20db1522 5886recipients are associated with the file (see
075746e2
KM
5887`epa-file-encrypt-to') or the function is invoked with a
5888\(KEYMODE-CUE) universal argument greater than 1.
5889
5890When encrypting, KEYMODE-CUE universal argument greater than 1
5891causes prompting for recipients for public-key keypair
5892encryption. Selecting no recipients results in symmetric key
29fac3fe
KM
5893encryption.
5894
075746e2
KM
5895Further, encrypting with a KEYMODE-CUE universal argument greater
5896than 4 - eg, preceded by a doubled Ctrl-U - causes association of
5897the specified recipients with the file, replacing those currently
99d99081 5898associated with it. This can be used to dissociate any
075746e2
KM
5899recipients with the file, by selecting no recipients in the
5900dialog.
5901
29fac3fe
KM
5902Encrypted topic's bullets are set to a `~' to signal that the
5903contents of the topic (body and subtopics, but not heading) is
5904pending encryption or encrypted. `*' asterisk immediately after
5905the bullet signals that the body is encrypted, its absence means
5906the topic is meant to be encrypted but is not currently. When a
5907file with topics pending encryption is saved, topics pending
20db1522 5908encryption are encrypted. See `allout-encrypt-unencrypted-on-saves'
29fac3fe 5909for auto-encryption specifics.
0949617b 5910
6eb0a10d 5911\*NOTE WELL* that automatic encryption that happens during saves will
60c7bbd2 5912default to symmetric encryption -- you must deliberately (re)encrypt key-pair
0949617b 5913encrypted topics if you want them to continue to use the key-pair cipher.
d82979ea 5914
d8350998 5915Level-one topics, with prefix consisting solely of an `*' asterisk, cannot be
d82979ea 5916encrypted. If you want to encrypt the contents of a top-level topic, use
29fac3fe 5917\\[allout-shift-in] to increase its depth."
69027dea
EZ
5918 (interactive "P")
5919 (save-excursion
5920 (allout-back-to-current-heading)
29fac3fe
KM
5921 (allout-toggle-subtree-encryption keymode-cue)))
5922;;;_ > allout-toggle-subtree-encryption (&optional keymode-cue)
5923(defun allout-toggle-subtree-encryption (&optional keymode-cue)
c07583cd 5924 "Encrypt clear text or decrypt encoded topic contents (body and subtopics.)
69027dea 5925
075746e2 5926Entry encryption defaults to symmetric key mode unless keypair
20db1522 5927recipients are associated with the file (see
075746e2
KM
5928`epa-file-encrypt-to') or the function is invoked with a
5929\(KEYMODE-CUE) universal argument greater than 1.
69027dea 5930
075746e2
KM
5931When encrypting, KEYMODE-CUE universal argument greater than 1
5932causes prompting for recipients for public-key keypair
5933encryption. Selecting no recipients results in symmetric key
5934encryption.
69027dea 5935
075746e2
KM
5936Further, encrypting with a KEYMODE-CUE universal argument greater
5937than 4 - eg, preceded by a doubled Ctrl-U - causes association of
5938the specified recipients with the file, replacing those currently
99d99081 5939associated with it. This can be used to dissociate any
075746e2
KM
5940recipients with the file, by selecting no recipients in the
5941dialog.
5942
20db1522 5943Encryption and decryption uses the Emacs 'epg' library.
69027dea 5944
29fac3fe 5945Encrypted text will be ascii-armored.
69027dea
EZ
5946
5947See `allout-toggle-current-subtree-encryption' for more details."
d82979ea
EZ
5948
5949 (interactive "P")
5950 (save-excursion
5951 (allout-end-of-prefix t)
5952
d8350998 5953 (if (= allout-recent-depth 1)
d82979ea
EZ
5954 (error (concat "Cannot encrypt or decrypt level 1 topics -"
5955 " shift it in to make it encryptable")))
5956
d82979ea 5957 (let* ((allout-buffer (current-buffer))
13aa217b
KM
5958 ;; for use with allout-auto-save-temporarily-disabled, if necessary:
5959 (was-buffer-saved-size buffer-saved-size)
498a6493 5960 ;; Assess location:
d8350998 5961 (bullet-pos allout-recent-prefix-beginning)
d82979ea
EZ
5962 (after-bullet-pos (point))
5963 (was-encrypted
5964 (progn (if (= (point-max) after-bullet-pos)
5965 (error "no body to encrypt"))
0949617b 5966 (allout-encrypted-topic-p)))
96b83743 5967 (was-collapsed (if (not (search-forward "\n" nil t))
d82979ea
EZ
5968 nil
5969 (backward-char 1)
96b83743 5970 (allout-hidden-p)))
d82979ea
EZ
5971 (subtree-beg (1+ (point)))
5972 (subtree-end (allout-end-of-subtree))
5973 (subject-text (buffer-substring-no-properties subtree-beg
5974 subtree-end))
5975 (subtree-end-char (char-after (1- subtree-end)))
96b83743 5976 (subtree-trailing-char (char-after subtree-end))
60c7bbd2 5977 ;; kluge -- result-text needs to be nil, but we also want to
96b83743
EZ
5978 ;; check for the error condition
5979 (result-text (if (or (string= "" subject-text)
5980 (string= "\n" subject-text))
5981 (error "No topic contents to %scrypt"
5982 (if was-encrypted "de" "en"))
5983 nil))
d82979ea 5984 ;; Assess key parameters:
498a6493
CY
5985 (was-coding-system buffer-file-coding-system))
5986
5987 (when (not was-encrypted)
5988 ;; ensure that non-ascii chars pending encryption are noticed before
116f996b 5989 ;; they're encrypted, so the coding system is set to accommodate
498a6493
CY
5990 ;; them.
5991 (setq buffer-file-coding-system
1c9b9df0 5992 (allout-select-safe-coding-system subtree-beg subtree-end))
498a6493
CY
5993 ;; if the coding system for the text being encrypted is different
5994 ;; than that prevailing, then there a real risk that the coding
5995 ;; system can't be noticed by emacs when the file is visited. to
5996 ;; mitigate that, offer to preserve the coding system using a file
5997 ;; local variable.
5998 (if (and (not (equal buffer-file-coding-system
5999 was-coding-system))
6000 (yes-or-no-p
6001 (format (concat "Register coding system %s as file local"
6002 " var? Necessary when only encrypted text"
6003 " is in that coding system. ")
6004 buffer-file-coding-system)))
6005 (allout-adjust-file-variable "buffer-file-coding-system"
6006 buffer-file-coding-system)))
d82979ea
EZ
6007
6008 (setq result-text
6009 (allout-encrypt-string subject-text was-encrypted
075746e2 6010 (current-buffer) keymode-cue))
d82979ea
EZ
6011
6012 ;; Replace the subtree with the processed product.
6013 (allout-unprotected
6014 (progn
6015 (set-buffer allout-buffer)
6016 (delete-region subtree-beg subtree-end)
6017 (insert result-text)
6018 (if was-collapsed
96b83743
EZ
6019 (allout-flag-region (1- subtree-beg) (point) t))
6020 ;; adjust trailing-blank-lines to preserve topic spacing:
d82979ea 6021 (if (not was-encrypted)
96b83743
EZ
6022 (if (and (= subtree-end-char ?\n)
6023 (= subtree-trailing-char ?\n))
6024 (insert subtree-trailing-char)))
d82979ea
EZ
6025 ;; Ensure that the item has an encrypted-entry bullet:
6026 (if (not (string= (buffer-substring-no-properties
6027 (1- after-bullet-pos) after-bullet-pos)
6028 allout-topic-encryption-bullet))
6029 (progn (goto-char (1- after-bullet-pos))
6030 (delete-char 1)
6031 (insert allout-topic-encryption-bullet)))
6032 (if was-encrypted
6033 ;; Remove the is-encrypted bullet qualifier:
6034 (progn (goto-char after-bullet-pos)
6035 (delete-char 1))
6036 ;; Add the is-encrypted bullet qualifier:
6037 (goto-char after-bullet-pos)
d8350998 6038 (insert "*"))))
13aa217b
KM
6039
6040 ;; adjust buffer's auto-save eligibility:
6041 (if was-encrypted
6042 (allout-inhibit-auto-save-info-for-decryption was-buffer-saved-size)
6043 (allout-maybe-resume-auto-save-info-after-encryption))
6044
7b97c764 6045 (run-hook-with-args 'allout-structure-added-functions
1b4d97b2 6046 bullet-pos subtree-end))))
6450907e
GM
6047
6048(declare-function epg-context-set-passphrase-callback "epg"
6049 (context passphrase-callback))
6050(declare-function epg-list-keys "epg" (context &optional name mode))
6051(declare-function epg-decrypt-string "epg" (context cipher))
6052(declare-function epg-encrypt-string "epg"
6053 (context plain recipients &optional sign always-trust))
6054(declare-function epg-user-id-string "epg" (user-id))
6055(declare-function epg-key-user-id-list "epg" (key))
6056
075746e2 6057;;;_ > allout-encrypt-string (text decrypt allout-buffer keymode-cue
29fac3fe 6058;;; &optional rejected)
075746e2 6059(defun allout-encrypt-string (text decrypt allout-buffer keymode-cue
29fac3fe 6060 &optional rejected)
0949617b 6061 "Encrypt or decrypt message TEXT.
d82979ea 6062
ff3e8c8e 6063Returns the resulting string, or nil if the transformation fails.
d82979ea 6064
ff3e8c8e 6065If DECRYPT is true (default false), then decrypt instead of encrypt.
d82979ea 6066
ff3e8c8e 6067ALLOUT-BUFFER identifies the buffer containing the text.
d82979ea 6068
075746e2 6069Entry encryption defaults to symmetric key mode unless keypair
20db1522 6070recipients are associated with the file (see
075746e2
KM
6071`epa-file-encrypt-to') or the function is invoked with a
6072\(KEYMODE-CUE) universal argument greater than 1.
d82979ea 6073
075746e2
KM
6074When encrypting, KEYMODE-CUE universal argument greater than 1
6075causes prompting for recipients for public-key keypair
6076encryption. Selecting no recipients results in symmetric key
6077encryption.
0949617b 6078
075746e2
KM
6079Further, encrypting with a KEYMODE-CUE universal argument greater
6080than 4 - eg, preceded by a doubled Ctrl-U - causes association of
6081the specified recipients with the file, replacing those currently
99d99081 6082associated with it. This can be used to dissociate any
075746e2
KM
6083recipients with the file, by selecting no recipients in the
6084dialog.
d82979ea 6085
29fac3fe 6086Optional REJECTED is for internal use, to convey the number of
48bd8440
EZ
6087rejections due to matches against
6088`allout-encryption-ciphertext-rejection-regexps', as limited by
6089`allout-encryption-ciphertext-rejection-ceiling'.
6090
2576c28f
KM
6091NOTE: A few GnuPG v2 versions improperly preserve incorrect
6092symmetric decryption keys, preventing entry of the correct key on
6093subsequent decryption attempts until the cache times-out. That
20db1522 6094can take several minutes. (Decryption of other entries is not
2576c28f
KM
6095affected.) Upgrade your EasyPG version, if you can, and you can
6096deliberately clear your gpg-agent's cache by sending it a '-HUP'
6097signal."
48bd8440 6098
ff3e8c8e 6099 (require 'epg)
29fac3fe 6100 (require 'epa)
0949617b 6101
075746e2
KM
6102 (let* ((epg-context (let* ((context (epg-make-context nil t)))
6103 (epg-context-set-passphrase-callback
6104 context #'epa-passphrase-callback-function)
6105 context))
13aa217b 6106
498a6493
CY
6107 (encoding (with-current-buffer allout-buffer
6108 buffer-file-coding-system))
6109 (multibyte (with-current-buffer allout-buffer
ff3e8c8e 6110 enable-multibyte-characters))
29fac3fe
KM
6111 ;; "sanitization" avoids encryption results that are outline structure.
6112 (sani-regexps 'allout-encryption-plaintext-sanitization-regexps)
6113 (strip-plaintext-regexps (if (not decrypt)
6114 (allout-get-configvar-values
6115 sani-regexps)))
6116 (rejection-regexps 'allout-encryption-ciphertext-rejection-regexps)
6117 (reject-ciphertext-regexps (if (not decrypt)
6118 (allout-get-configvar-values
6119 rejection-regexps)))
48bd8440
EZ
6120 (rejected (or rejected 0))
6121 (rejections-left (- allout-encryption-ciphertext-rejection-ceiling
6122 rejected))
075746e2
KM
6123 (keypair-mode (cond (decrypt 'decrypting)
6124 ((<= (prefix-numeric-value keymode-cue) 1)
6125 'default)
6126 ((<= (prefix-numeric-value keymode-cue) 4)
6127 'prompt)
6128 ((> (prefix-numeric-value keymode-cue) 4)
6129 'prompt-save)))
6130 (keypair-message (concat "Select encryption recipients.\n"
6131 "Symmetric encryption is done if no"
6132 " recipients are selected. "))
6133 (encrypt-to (and (boundp 'epa-file-encrypt-to) epa-file-encrypt-to))
29fac3fe
KM
6134 recipients
6135 massaged-text
6136 result-text
38b2ca53 6137 )
0949617b 6138
ff3e8c8e
KM
6139 ;; Massage the subject text for encoding and filtering.
6140 (with-temp-buffer
6141 (insert text)
6142 ;; convey the text characteristics of the original buffer:
96497653 6143 (set-buffer-multibyte multibyte)
ff3e8c8e
KM
6144 (when encoding
6145 (set-buffer-file-coding-system encoding)
6146 (if (not decrypt)
6147 (encode-coding-region (point-min) (point-max) encoding)))
6148
6149 ;; remove sanitization regexps matches before encrypting:
6150 (when (and strip-plaintext-regexps (not decrypt))
6151 (dolist (re strip-plaintext-regexps)
6152 (let ((re (if (listp re) (car re) re))
6153 (replacement (if (listp re) (cadr re) "")))
6154 (goto-char (point-min))
6155 (save-match-data
6156 (while (re-search-forward re nil t)
6157 (replace-match replacement nil nil))))))
6158 (setq massaged-text (buffer-substring-no-properties (point-min)
6159 (point-max))))
075746e2
KM
6160 ;; determine key mode and, if keypair, recipients:
6161 (setq recipients
6162 (case keypair-mode
6163
6164 (decrypting nil)
6165
6166 (default (if encrypt-to (epg-list-keys epg-context encrypt-to)))
6167
6168 ((prompt prompt-save)
6169 (save-window-excursion
6170 (epa-select-keys epg-context keypair-message)))))
6171
ff3e8c8e 6172 (setq result-text
ff3e8c8e 6173 (if decrypt
2a095cec
KM
6174 (condition-case err
6175 (epg-decrypt-string epg-context
6176 (encode-coding-string massaged-text
6177 (or encoding 'utf-8)))
6178 (epg-error
6179 (signal 'egp-error
6180 (cons (concat (cadr err) " - gpg version problem?")
6181 (cddr err)))))
0af8c88b
KM
6182 (replace-regexp-in-string "\n$" ""
6183 (epg-encrypt-string epg-context
6184 (encode-coding-string massaged-text
6185 (or encoding 'utf-8))
6186 recipients))))
ff3e8c8e
KM
6187
6188 ;; validate result -- non-empty
075746e2
KM
6189 (if (not result-text)
6190 (error "%scryption failed." (if decrypt "De" "En")))
6191
29fac3fe 6192
075746e2
KM
6193 (when (eq keypair-mode 'prompt-save)
6194 ;; set epa-file-encrypt-to in the buffer:
6195 (setq epa-file-encrypt-to (mapcar (lambda (key)
6196 (epg-user-id-string
6197 (car (epg-key-user-id-list key))))
6198 recipients))
6199 ;; change the file variable:
6200 (allout-adjust-file-variable "epa-file-encrypt-to" epa-file-encrypt-to))
d82979ea 6201
075746e2 6202 (cond
29fac3fe
KM
6203 ;; Retry (within limit) if ciphertext contains rejections:
6204 ((and (not decrypt)
6205 ;; Check for disqualification of this ciphertext:
6206 (let ((regexps reject-ciphertext-regexps)
6207 reject-it)
6208 (while (and regexps (not reject-it))
6209 (setq reject-it (string-match (car regexps) result-text))
6210 (pop regexps))
6211 reject-it))
6212 (setq rejections-left (1- rejections-left))
6213 (if (<= rejections-left 0)
6214 (error (concat "Ciphertext rejected too many times"
6215 " (%s), per `%s'")
6216 allout-encryption-ciphertext-rejection-ceiling
6217 'allout-encryption-ciphertext-rejection-regexps)
6218 ;; try again (gpg-agent may have the key cached):
6219 (allout-encrypt-string text decrypt allout-buffer keypair-mode
6220 (1+ rejected))))
6221
6222 ;; Barf if encryption yields extraordinary control chars:
6223 ((and (not decrypt)
6224 (string-match "[\C-a\C-k\C-o-\C-z\C-@]"
6225 result-text))
6226 (error (concat "Encryption produced non-armored text, which"
6227 "conflicts with allout mode -- reconfigure!")))
29fac3fe 6228 (t result-text))))
13aa217b
KM
6229;;;_ > allout-inhibit-auto-save-info-for-decryption
6230(defun allout-inhibit-auto-save-info-for-decryption (was-buffer-saved-size)
6231 "Temporarily prevent auto-saves in this buffer when an item is decrypted.
6232
20db1522 6233WAS-BUFFER-SAVED-SIZE is the value of `buffer-saved-size' *before*
13aa217b
KM
6234the decryption."
6235 (when (not (or (= buffer-saved-size -1) (= was-buffer-saved-size -1)))
6236 (setq allout-auto-save-temporarily-disabled was-buffer-saved-size
6237 buffer-saved-size -1)))
6238;;;_ > allout-maybe-resume-auto-save-info-after-encryption ()
6239(defun allout-maybe-resume-auto-save-info-after-encryption ()
6240 "Restore auto-save info, *if* there are no topics pending encryption."
6241 (when (and allout-auto-save-temporarily-disabled
6242 (= buffer-saved-size -1)
6243 (save-excursion
6244 (save-restriction
6245 (widen)
6246 (goto-char (point-min))
6247 (not (allout-next-topic-pending-encryption)))))
6248 (setq buffer-saved-size allout-auto-save-temporarily-disabled
6249 allout-auto-save-temporarily-disabled nil)))
6250
d82979ea
EZ
6251;;;_ > allout-encrypted-topic-p ()
6252(defun allout-encrypted-topic-p ()
6253 "True if the current topic is encryptable and encrypted."
6254 (save-excursion
6255 (allout-end-of-prefix t)
6256 (and (string= (buffer-substring-no-properties (1- (point)) (point))
6257 allout-topic-encryption-bullet)
2a1408fd 6258 (save-match-data (looking-at "\\*")))
d82979ea
EZ
6259 )
6260 )
13aa217b
KM
6261;;;_ > allout-next-topic-pending-encryption ()
6262(defun allout-next-topic-pending-encryption ()
d82979ea
EZ
6263 "Return the point of the next topic pending encryption, or nil if none.
6264
75bcb912 6265Such a topic has the `allout-topic-encryption-bullet' without an
20db1522
JB
6266immediately following '*' that would mark the topic as being encrypted.
6267It must also have content."
d82979ea 6268 (let (done got content-beg)
2a1408fd
DN
6269 (save-match-data
6270 (while (not done)
d82979ea 6271
2a1408fd
DN
6272 (if (not (re-search-forward
6273 (format "\\(\\`\\|\n\\)%s *%s[^*]"
6274 (regexp-quote allout-header-prefix)
6275 (regexp-quote allout-topic-encryption-bullet))
6276 nil t))
6277 (setq got nil
6278 done t)
6279 (goto-char (setq got (match-beginning 0)))
6280 (if (save-match-data (looking-at "\n"))
6281 (forward-char 1))
6282 (setq got (point)))
6283
6284 (cond ((not got)
6285 (setq done t))
6286
6287 ((not (search-forward "\n"))
6288 (setq got nil
6289 done t))
6290
6291 ((eobp)
6292 (setq got nil
6293 done t))
6294
6295 (t
6296 (setq content-beg (point))
6297 (backward-char 1)
6298 (allout-end-of-subtree)
13aa217b 6299 (if (<= (point) content-beg)
2a1408fd
DN
6300 ;; Continue looking
6301 (setq got nil)
6302 ;; Got it!
6303 (setq done t)))
6304 )
6305 )
6306 (if got
6307 (goto-char got))
d82979ea 6308 )
d82979ea
EZ
6309 )
6310 )
13aa217b
KM
6311;;;_ > allout-encrypt-decrypted ()
6312(defun allout-encrypt-decrypted ()
d82979ea
EZ
6313 "Encrypt topics pending encryption except those containing exemption point.
6314
d82979ea
EZ
6315If a topic that is currently being edited was encrypted, we return a list
6316containing the location of the topic and the location of the cursor just
6317before the topic was encrypted. This can be used, eg, to decrypt the topic
6318and exactly resituate the cursor if this is being done as part of a file
6319save. See `allout-encrypt-unencrypted-on-saves' for more info."
6320
6321 (interactive "p")
2a1408fd
DN
6322 (save-match-data
6323 (save-excursion
6324 (let* ((current-mark (point-marker))
6325 (current-mark-position (marker-position current-mark))
6326 was-modified
6327 bo-subtree
6328 editing-topic editing-point)
6329 (goto-char (point-min))
13aa217b 6330 (while (allout-next-topic-pending-encryption)
2a1408fd
DN
6331 (setq was-modified (buffer-modified-p))
6332 (when (save-excursion
6333 (and (boundp 'allout-encrypt-unencrypted-on-saves)
6334 allout-encrypt-unencrypted-on-saves
6335 (setq bo-subtree (re-search-forward "$"))
6336 (not (allout-hidden-p))
6337 (>= current-mark (point))
6338 (allout-end-of-current-subtree)
6339 (<= current-mark (point))))
d82979ea
EZ
6340 (setq editing-topic (point)
6341 ;; we had to wait for this 'til now so prior topics are
6342 ;; encrypted, any relevant text shifts are in place:
96b83743
EZ
6343 editing-point (- current-mark-position
6344 (count-trailing-whitespace-region
6345 bo-subtree current-mark-position))))
2a1408fd
DN
6346 (allout-toggle-subtree-encryption)
6347 (if (not was-modified)
6348 (set-buffer-modified-p nil))
6349 )
d82979ea
EZ
6350 (if (not was-modified)
6351 (set-buffer-modified-p nil))
2a1408fd 6352 (if editing-topic (list editing-topic editing-point))
d82979ea 6353 )
d82979ea
EZ
6354 )
6355 )
6356 )
6357
6358;;;_ #9 miscellaneous
d8350998
CY
6359;;;_ : Mode:
6360;;;_ > outlineify-sticky ()
19b84ba3 6361;; outlinify-sticky is correct spelling; provide this alias for sticklers:
0949617b 6362;;;###autoload
19b84ba3 6363(defalias 'outlinify-sticky 'outlineify-sticky)
0949617b 6364;;;###autoload
9d3aa82c 6365(defun outlineify-sticky (&optional _arg)
a0776d6b 6366 "Activate outline mode and establish file var so it is started subsequently.
1977b8f6 6367
11c46e15 6368See `allout-layout' and customization of `allout-auto-activation'
20db1522 6369for details on preparing Emacs for automatic allout activation."
19b84ba3
RS
6370
6371 (interactive "P")
6372
2576c28f
KM
6373 (if (allout-mode-p) (allout-mode)) ; deactivate so we can re-activate...
6374 (allout-mode)
19b84ba3
RS
6375
6376 (save-excursion
6377 (goto-char (point-min))
ede4ac6a 6378 (if (allout-goto-prefix)
19b84ba3 6379 t
fd5359c6 6380 (allout-open-topic 2)
60c7bbd2 6381 (insert (concat "Dummy outline topic header -- see"
d82979ea
EZ
6382 "`allout-mode' docstring: `^Hm'."))
6383 (allout-adjust-file-variable
4034b0e2 6384 "allout-layout" (or allout-layout '(-1 : 0))))))
d8350998 6385;;;_ > allout-file-vars-section-data ()
d82979ea
EZ
6386(defun allout-file-vars-section-data ()
6387 "Return data identifying the file-vars section, or nil if none.
6388
60c7bbd2 6389Returns a list of the form (BEGINNING-POINT PREFIX-STRING SUFFIX-STRING)."
d82979ea
EZ
6390 ;; minimally gleaned from emacs 21.4 files.el hack-local-variables function.
6391 (let (beg prefix suffix)
6392 (save-excursion
1977b8f6 6393 (goto-char (point-max))
d82979ea
EZ
6394 (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move)
6395 (if (let ((case-fold-search t))
6396 (not (search-forward "Local Variables:" nil t)))
6397 nil
6398 (setq beg (- (point) 16))
6399 (setq suffix (buffer-substring-no-properties
6400 (point)
96b83743 6401 (progn (if (search-forward "\n" nil t)
d82979ea
EZ
6402 (forward-char -1))
6403 (point))))
6404 (setq prefix (buffer-substring-no-properties
96b83743 6405 (progn (if (search-backward "\n" nil t)
d82979ea
EZ
6406 (forward-char 1))
6407 (point))
6408 beg))
6409 (list beg prefix suffix))
6410 )
6411 )
6412 )
d8350998 6413;;;_ > allout-adjust-file-variable (varname value)
d82979ea 6414(defun allout-adjust-file-variable (varname value)
a0a022d5 6415 "Adjust the setting of an Emacs file variable named VARNAME to VALUE.
d82979ea 6416
20db1522 6417This activity is inhibited if either `enable-local-variables' or
d82979ea
EZ
6418`allout-enable-file-variable-adjustment' are nil.
6419
6420When enabled, an entry for the variable is created if not already present,
6421or changed if established with a different value. The section for the file
6422variables, itself, is created if not already present. When created, the
c07583cd 6423section lines (including the section line) exist as second-level topics in
d82979ea
EZ
6424a top-level topic at the end of the file.
6425
a0a022d5 6426`enable-local-variables' must be true for any of this to happen."
d82979ea
EZ
6427 (if (not (and enable-local-variables
6428 allout-enable-file-variable-adjustment))
6429 nil
6430 (save-excursion
0458fa80
EZ
6431 (let ((inhibit-field-text-motion t)
6432 (section-data (allout-file-vars-section-data))
d82979ea
EZ
6433 beg prefix suffix)
6434 (if section-data
6435 (setq beg (car section-data)
6436 prefix (cadr section-data)
6437 suffix (car (cddr section-data)))
6438 ;; create the section
6439 (goto-char (point-max))
6440 (open-line 1)
6441 (allout-open-topic 0)
6442 (end-of-line)
6443 (insert "Local emacs vars.\n")
6444 (allout-open-topic 1)
6445 (setq beg (point)
6446 suffix ""
6447 prefix (buffer-substring-no-properties (progn
6448 (beginning-of-line)
6449 (point))
6450 beg))
6451 (goto-char beg)
6452 (insert "Local variables:\n")
6453 (allout-open-topic 0)
6454 (insert "End:\n")
6455 )
6456 ;; look for existing entry or create one, leaving point for insertion
6457 ;; of new value:
6458 (goto-char beg)
6459 (allout-show-to-offshoot)
6460 (if (search-forward (concat "\n" prefix varname ":") nil t)
6461 (let* ((value-beg (point))
96b83743 6462 (line-end (progn (if (search-forward "\n" nil t)
d82979ea
EZ
6463 (forward-char -1))
6464 (point)))
6465 (value-end (- line-end (length suffix))))
6466 (if (> value-end value-beg)
6467 (delete-region value-beg value-end)))
6468 (end-of-line)
6469 (open-line 1)
6470 (forward-line 1)
6471 (insert (concat prefix varname ":")))
6472 (insert (format " %S%s" value suffix))
6473 )
6474 )
6475 )
6476 )
d8350998
CY
6477;;;_ > allout-get-configvar-values (varname)
6478(defun allout-get-configvar-values (configvar-name)
6479 "Return a list of values of the symbols in list bound to CONFIGVAR-NAME.
6480
6481The user is prompted for removal of symbols that are unbound, and they
6482otherwise are ignored.
6483
6484CONFIGVAR-NAME should be the name of the configuration variable,
6485not its value."
6486
6487 (let ((configvar-value (symbol-value configvar-name))
6488 got)
6489 (dolist (sym configvar-value)
6490 (if (not (boundp sym))
60c7bbd2 6491 (if (yes-or-no-p (format "%s entry `%s' is unbound -- remove it? "
d8350998
CY
6492 configvar-name sym))
6493 (delq sym (symbol-value configvar-name)))
6494 (push (symbol-value sym) got)))
6495 (reverse got)))
6496;;;_ : Topics:
6497;;;_ > allout-mark-topic ()
6498(defun allout-mark-topic ()
6499 "Put the region around topic currently containing point."
6500 (interactive)
6501 (let ((inhibit-field-text-motion t))
6502 (beginning-of-line))
6503 (allout-goto-prefix-doublechecked)
6504 (push-mark (point))
6505 (allout-end-of-current-subtree)
6506 (exchange-point-and-mark))
6507;;;_ : UI:
6508;;;_ > solicit-char-in-string (prompt string &optional do-defaulting)
1977b8f6 6509(defun solicit-char-in-string (prompt string &optional do-defaulting)
c567ac01 6510 "Solicit (with first arg PROMPT) choice of a character from string STRING.
1977b8f6 6511
c567ac01 6512Optional arg DO-DEFAULTING indicates to accept empty input (CR)."
1977b8f6
RS
6513
6514 (let ((new-prompt prompt)
6515 got)
6516
6517 (while (not got)
6518 (message "%s" new-prompt)
6519
6520 ;; We do our own reading here, so we can circumvent, eg, special
9179616f 6521 ;; treatment for `?' character. (Oughta use minibuffer keymap instead.)
1977b8f6 6522 (setq got
c567ac01 6523 (char-to-string (let ((cursor-in-echo-area nil)) (read-char))))
1977b8f6 6524
9179616f
DL
6525 (setq got
6526 (cond ((string-match (regexp-quote got) string) got)
6527 ((and do-defaulting (string= got "\r"))
6528 ;; Return empty string to default:
6529 "")
6530 ((string= got "\C-g") (signal 'quit nil))
6531 (t
6532 (setq new-prompt (concat prompt
6533 got
6534 " ...pick from: "
6535 string
6536 ""))
6537 nil))))
60c7bbd2 6538 ;; got something out of loop -- return it:
9179616f 6539 got)
1977b8f6 6540 )
d8350998
CY
6541;;;_ : Strings:
6542;;;_ > regexp-sans-escapes (string)
c567ac01
RS
6543(defun regexp-sans-escapes (regexp &optional successive-backslashes)
6544 "Return a copy of REGEXP with all character escapes stripped out.
19b84ba3 6545
60c7bbd2 6546Representations of actual backslashes -- '\\\\\\\\' -- are left as a
c567ac01
RS
6547single backslash.
6548
d82979ea 6549Optional arg SUCCESSIVE-BACKSLASHES is used internally for recursion."
c567ac01
RS
6550
6551 (if (string= regexp "")
6552 ""
6553 ;; Set successive-backslashes to number if current char is
6554 ;; backslash, or else to nil:
6555 (setq successive-backslashes
6556 (if (= (aref regexp 0) ?\\)
6557 (if successive-backslashes (1+ successive-backslashes) 1)
6558 nil))
6559 (if (or (not successive-backslashes) (= 2 successive-backslashes))
6560 ;; Include first char:
6561 (concat (substring regexp 0 1)
6562 (regexp-sans-escapes (substring regexp 1)))
6563 ;; Exclude first char, but maintain count:
6564 (regexp-sans-escapes (substring regexp 1) successive-backslashes))))
d8350998 6565;;;_ > count-trailing-whitespace-region (beg end)
96b83743
EZ
6566(defun count-trailing-whitespace-region (beg end)
6567 "Return number of trailing whitespace chars between BEG and END.
6568
6569If BEG is bigger than END we return 0."
6570 (if (> beg end)
6571 0
2a1408fd
DN
6572 (save-match-data
6573 (save-excursion
6574 (goto-char beg)
6575 (let ((count 0))
6576 (while (re-search-forward "[ ][ ]*$" end t)
6577 (goto-char (1+ (match-beginning 2)))
6578 (setq count (1+ count)))
6579 count)))))
d8350998
CY
6580;;;_ > allout-format-quote (string)
6581(defun allout-format-quote (string)
6582 "Return a copy of string with all \"%\" characters doubled."
6583 (apply 'concat
6584 (mapcar (lambda (char) (if (= char ?%) "%%" (char-to-string char)))
6585 string)))
6586;;;_ : lists
6587;;;_ > allout-flatten (list)
6588(defun allout-flatten (list)
6589 "Return a list of all atoms in list."
6590 ;; classic.
6591 (cond ((null list) nil)
8a559d4e
EZ
6592 ((atom (car list)) (cons (car list) (allout-flatten (cdr list))))
6593 (t (append (allout-flatten (car list)) (allout-flatten (cdr list))))))
75bcb912 6594;;;_ : Compatibility:
1c9b9df0
KM
6595;;;_ : xemacs undo-in-progress provision:
6596(unless (boundp 'undo-in-progress)
6597 (defvar undo-in-progress nil
6598 "Placeholder defvar for XEmacs compatibility from allout.el.")
6599 (defadvice undo-more (around allout activate)
6600 ;; This defadvice used only in emacs that lack undo-in-progress, eg xemacs.
6601 (let ((undo-in-progress t)) ad-do-it)))
6602
d8350998 6603;;;_ > allout-mark-marker to accommodate divergent emacsen:
96b83743
EZ
6604(defun allout-mark-marker (&optional force buffer)
6605 "Accommodate the different signature for `mark-marker' across Emacsen.
6606
3bda1277 6607XEmacs takes two optional args, while Emacs does not,
96b83743
EZ
6608so pass them along when appropriate."
6609 (if (featurep 'xemacs)
6610 (apply 'mark-marker force buffer)
6611 (mark-marker)))
d8350998 6612;;;_ > subst-char-in-string if necessary
d82979ea
EZ
6613(if (not (fboundp 'subst-char-in-string))
6614 (defun subst-char-in-string (fromchar tochar string &optional inplace)
6615 "Replace FROMCHAR with TOCHAR in STRING each time it occurs.
6616Unless optional argument INPLACE is non-nil, return a new string."
6617 (let ((i (length string))
6618 (newstr (if inplace string (copy-sequence string))))
6619 (while (> i 0)
6620 (setq i (1- i))
6621 (if (eq (aref newstr i) fromchar)
6622 (aset newstr i tochar)))
6623 newstr)))
d8350998 6624;;;_ > wholenump if necessary
96b83743
EZ
6625(if (not (fboundp 'wholenump))
6626 (defalias 'wholenump 'natnump))
d8350998 6627;;;_ > remove-overlays if necessary
96b83743
EZ
6628(if (not (fboundp 'remove-overlays))
6629 (defun remove-overlays (&optional beg end name val)
6630 "Clear BEG and END of overlays whose property NAME has value VAL.
6631Overlays might be moved and/or split.
6632BEG and END default respectively to the beginning and end of buffer."
6633 (unless beg (setq beg (point-min)))
6634 (unless end (setq end (point-max)))
6635 (if (< end beg)
6636 (setq beg (prog1 end (setq end beg))))
6637 (save-excursion
6638 (dolist (o (overlays-in beg end))
6639 (when (eq (overlay-get o name) val)
6640 ;; Either push this overlay outside beg...end
6641 ;; or split it to exclude beg...end
6642 ;; or delete it entirely (if it is contained in beg...end).
6643 (if (< (overlay-start o) beg)
6644 (if (> (overlay-end o) end)
6645 (progn
6646 (move-overlay (copy-overlay o)
6647 (overlay-start o) beg)
6648 (move-overlay o end (overlay-end o)))
6649 (move-overlay o (overlay-start o) beg))
6650 (if (> (overlay-end o) end)
6651 (move-overlay o end (overlay-end o))
6652 (delete-overlay o)))))))
6653 )
60c7bbd2 6654;;;_ > copy-overlay if necessary -- xemacs ~ 21.4
96b83743
EZ
6655(if (not (fboundp 'copy-overlay))
6656 (defun copy-overlay (o)
6657 "Return a copy of overlay O."
6658 (let ((o1 (make-overlay (overlay-start o) (overlay-end o)
6659 ;; FIXME: there's no easy way to find the
6660 ;; insertion-type of the two markers.
6661 (overlay-buffer o)))
6662 (props (overlay-properties o)))
6663 (while props
6664 (overlay-put o1 (pop props) (pop props)))
6665 o1)))
60c7bbd2 6666;;;_ > add-to-invisibility-spec if necessary -- xemacs ~ 21.4
96b83743
EZ
6667(if (not (fboundp 'add-to-invisibility-spec))
6668 (defun add-to-invisibility-spec (element)
6669 "Add ELEMENT to `buffer-invisibility-spec'.
6670See documentation for `buffer-invisibility-spec' for the kind of elements
6671that can be added."
6672 (if (eq buffer-invisibility-spec t)
6673 (setq buffer-invisibility-spec (list t)))
6674 (setq buffer-invisibility-spec
6675 (cons element buffer-invisibility-spec))))
60c7bbd2 6676;;;_ > remove-from-invisibility-spec if necessary -- xemacs ~ 21.4
96b83743
EZ
6677(if (not (fboundp 'remove-from-invisibility-spec))
6678 (defun remove-from-invisibility-spec (element)
6679 "Remove ELEMENT from `buffer-invisibility-spec'."
6680 (if (consp buffer-invisibility-spec)
6681 (setq buffer-invisibility-spec (delete element
6682 buffer-invisibility-spec)))))
60c7bbd2 6683;;;_ > move-beginning-of-line if necessary -- older emacs, xemacs
96b83743
EZ
6684(if (not (fboundp 'move-beginning-of-line))
6685 (defun move-beginning-of-line (arg)
6686 "Move point to beginning of current line as displayed.
6687\(This disregards invisible newlines such as those
6688which are part of the text that an image rests on.)
6689
6690With argument ARG not nil or 1, move forward ARG - 1 lines first.
6691If point reaches the beginning or end of buffer, it stops there.
4034b0e2 6692To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
96b83743
EZ
6693 (interactive "p")
6694 (or arg (setq arg 1))
6695 (if (/= arg 1)
6696 (condition-case nil (line-move (1- arg)) (error nil)))
6697
99d99081 6698 ;; Move to beginning-of-line, ignoring fields and invisible text.
8570ce80 6699 (skip-chars-backward "^\n")
2a1408fd
DN
6700 (while (and (not (bobp))
6701 (let ((prop
6702 (get-char-property (1- (point)) 'invisible)))
6703 (if (eq buffer-invisibility-spec t)
6704 prop
6705 (or (memq prop buffer-invisibility-spec)
6706 (assq prop buffer-invisibility-spec)))))
8570ce80
SM
6707 (goto-char (if (featurep 'xemacs)
6708 (previous-property-change (point))
6709 (previous-char-property-change (point))))
6710 (skip-chars-backward "^\n"))
6711 (vertical-motion 0))
96b83743 6712)
9b026d9f 6713;;;_ > move-end-of-line if necessary -- Emacs < 22.1, xemacs
96b83743
EZ
6714(if (not (fboundp 'move-end-of-line))
6715 (defun move-end-of-line (arg)
6716 "Move point to end of current line as displayed.
6717\(This disregards invisible newlines such as those
6718which are part of the text that an image rests on.)
6719
6720With argument ARG not nil or 1, move forward ARG - 1 lines first.
6721If point reaches the beginning or end of buffer, it stops there.
4034b0e2 6722To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
96b83743
EZ
6723 (interactive "p")
6724 (or arg (setq arg 1))
8570ce80 6725 (let (done)
96b83743
EZ
6726 (while (not done)
6727 (let ((newpos
6728 (save-excursion
6729 (let ((goal-column 0))
6730 (and (condition-case nil
6731 (or (line-move arg) t)
6732 (error nil))
6733 (not (bobp))
6734 (progn
2a1408fd
DN
6735 (while
6736 (and
6737 (not (bobp))
6738 (let ((prop
6739 (get-char-property (1- (point))
6740 'invisible)))
6741 (if (eq buffer-invisibility-spec t)
6742 prop
6743 (or (memq prop
6744 buffer-invisibility-spec)
6745 (assq prop
6746 buffer-invisibility-spec)))))
8570ce80
SM
6747 (goto-char
6748 (previous-char-property-change (point))))
96b83743
EZ
6749 (backward-char 1)))
6750 (point)))))
6751 (goto-char newpos)
6752 (if (and (> (point) newpos)
6753 (eq (preceding-char) ?\n))
6754 (backward-char 1)
6755 (if (and (> (point) newpos) (not (eobp))
6756 (not (eq (following-char) ?\n)))
6757 ;; If we skipped something intangible
6758 ;; and now we're not really at eol,
6759 ;; keep going.
6760 (setq arg 1)
8570ce80 6761 (setq done t)))))))
96b83743 6762 )
1c9b9df0
KM
6763;;;_ > allout-next-single-char-property-change -- alias unless lacking
6764(defalias 'allout-next-single-char-property-change
6765 (if (fboundp 'next-single-char-property-change)
6766 'next-single-char-property-change
6767 'next-single-property-change)
6768 ;; No docstring because xemacs defalias doesn't support it.
6769 )
6770;;;_ > allout-previous-single-char-property-change -- alias unless lacking
6771(defalias 'allout-previous-single-char-property-change
6772 (if (fboundp 'previous-single-char-property-change)
6773 'previous-single-char-property-change
6774 'previous-single-property-change)
6775 ;; No docstring because xemacs defalias doesn't support it.
6776 )
1c9b9df0
KM
6777;;;_ > allout-select-safe-coding-system
6778(defalias 'allout-select-safe-coding-system
6779 (if (fboundp 'select-safe-coding-system)
6780 'select-safe-coding-system
6781 'detect-coding-region)
6782 )
6783;;;_ > allout-substring-no-properties
6784;; define as alias first, so byte compiler is happy.
6785(defalias 'allout-substring-no-properties 'substring-no-properties)
6786;; then supplant with definition if underlying alias absent.
6787(if (not (fboundp 'substring-no-properties))
6788 (defun allout-substring-no-properties (string &optional start end)
6789 (substring string (or start 0) end))
6790 )
96b83743 6791
96b83743 6792;;;_ #10 Unfinished
fd5359c6
MR
6793;;;_ > allout-bullet-isearch (&optional bullet)
6794(defun allout-bullet-isearch (&optional bullet)
c07583cd 6795 "Isearch (regexp) for topic with bullet BULLET."
c567ac01
RS
6796 (interactive)
6797 (if (not bullet)
6798 (setq bullet (solicit-char-in-string
6799 "ISearch for topic with bullet: "
fd5359c6 6800 (regexp-sans-escapes allout-bullets-string))))
353e2ef2 6801
c567ac01
RS
6802 (let ((isearch-regexp t)
6803 (isearch-string (concat "^"
fd5359c6 6804 allout-header-prefix
c567ac01
RS
6805 "[ \t]*"
6806 bullet)))
6807 (isearch-repeat 'forward)
6808 (isearch-mode t)))
96b83743 6809
60c7bbd2 6810;;;_ #11 Unit tests -- this should be last item before "Provide"
01fc9422
EZ
6811;;;_ > allout-run-unit-tests ()
6812(defun allout-run-unit-tests ()
6813 "Run the various allout unit tests."
6814 (message "Running allout tests...")
6815 (allout-test-resumptions)
6816 (message "Running allout tests... Done.")
6817 (sit-for .5))
6818;;;_ : test resumptions:
6819;;;_ > allout-tests-obliterate-variable (name)
6820(defun allout-tests-obliterate-variable (name)
6821 "Completely unbind variable with NAME."
1c9b9df0 6822 (if (local-variable-p name (current-buffer)) (kill-local-variable name))
01fc9422
EZ
6823 (while (boundp name) (makunbound name)))
6824;;;_ > allout-test-resumptions ()
6825(defvar allout-tests-globally-unbound nil
60c7bbd2 6826 "Fodder for allout resumptions tests -- defvar just for byte compiler.")
01fc9422 6827(defvar allout-tests-globally-true nil
60c7bbd2 6828 "Fodder for allout resumptions tests -- defvar just for byte compiler.")
01fc9422 6829(defvar allout-tests-locally-true nil
60c7bbd2 6830 "Fodder for allout resumptions tests -- defvar just for byte compiler.")
01fc9422
EZ
6831(defun allout-test-resumptions ()
6832 "Exercise allout resumptions."
6833 ;; for each resumption case, we also test that the right local/global
6834 ;; scopes are affected during resumption effects:
6835
6836 ;; ensure that previously unbound variables return to the unbound state.
6837 (with-temp-buffer
6838 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6839 (allout-add-resumptions '(allout-tests-globally-unbound t))
6840 (assert (not (default-boundp 'allout-tests-globally-unbound)))
1c9b9df0 6841 (assert (local-variable-p 'allout-tests-globally-unbound (current-buffer)))
01fc9422
EZ
6842 (assert (boundp 'allout-tests-globally-unbound))
6843 (assert (equal allout-tests-globally-unbound t))
6844 (allout-do-resumptions)
1c9b9df0
KM
6845 (assert (not (local-variable-p 'allout-tests-globally-unbound
6846 (current-buffer))))
01fc9422
EZ
6847 (assert (not (boundp 'allout-tests-globally-unbound))))
6848
6849 ;; ensure that variable with prior global value is resumed
6850 (with-temp-buffer
6851 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6852 (setq allout-tests-globally-true t)
6853 (allout-add-resumptions '(allout-tests-globally-true nil))
6854 (assert (equal (default-value 'allout-tests-globally-true) t))
1c9b9df0 6855 (assert (local-variable-p 'allout-tests-globally-true (current-buffer)))
01fc9422
EZ
6856 (assert (equal allout-tests-globally-true nil))
6857 (allout-do-resumptions)
1c9b9df0
KM
6858 (assert (not (local-variable-p 'allout-tests-globally-true
6859 (current-buffer))))
01fc9422
EZ
6860 (assert (boundp 'allout-tests-globally-true))
6861 (assert (equal allout-tests-globally-true t)))
6862
6863 ;; ensure that prior local value is resumed
6864 (with-temp-buffer
6865 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6866 (set (make-local-variable 'allout-tests-locally-true) t)
6867 (assert (not (default-boundp 'allout-tests-locally-true))
60c7bbd2 6868 nil (concat "Test setup mistake -- variable supposed to"
01fc9422 6869 " not have global binding, but it does."))
1c9b9df0 6870 (assert (local-variable-p 'allout-tests-locally-true (current-buffer))
60c7bbd2 6871 nil (concat "Test setup mistake -- variable supposed to have"
01fc9422
EZ
6872 " local binding, but it lacks one."))
6873 (allout-add-resumptions '(allout-tests-locally-true nil))
6874 (assert (not (default-boundp 'allout-tests-locally-true)))
1c9b9df0 6875 (assert (local-variable-p 'allout-tests-locally-true (current-buffer)))
01fc9422
EZ
6876 (assert (equal allout-tests-locally-true nil))
6877 (allout-do-resumptions)
6878 (assert (boundp 'allout-tests-locally-true))
1c9b9df0 6879 (assert (local-variable-p 'allout-tests-locally-true (current-buffer)))
01fc9422
EZ
6880 (assert (equal allout-tests-locally-true t))
6881 (assert (not (default-boundp 'allout-tests-locally-true))))
6882
6883 ;; ensure that last of multiple resumptions holds, for various scopes.
6884 (with-temp-buffer
6885 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6886 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6887 (setq allout-tests-globally-true t)
6888 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6889 (set (make-local-variable 'allout-tests-locally-true) t)
6890 (allout-add-resumptions '(allout-tests-globally-unbound t)
6891 '(allout-tests-globally-true nil)
6892 '(allout-tests-locally-true nil))
6893 (allout-add-resumptions '(allout-tests-globally-unbound 2)
6894 '(allout-tests-globally-true 3)
6895 '(allout-tests-locally-true 4))
6896 ;; reestablish many of the basic conditions are maintained after re-add:
6897 (assert (not (default-boundp 'allout-tests-globally-unbound)))
1c9b9df0 6898 (assert (local-variable-p 'allout-tests-globally-unbound (current-buffer)))
01fc9422
EZ
6899 (assert (equal allout-tests-globally-unbound 2))
6900 (assert (default-boundp 'allout-tests-globally-true))
1c9b9df0 6901 (assert (local-variable-p 'allout-tests-globally-true (current-buffer)))
01fc9422
EZ
6902 (assert (equal allout-tests-globally-true 3))
6903 (assert (not (default-boundp 'allout-tests-locally-true)))
1c9b9df0 6904 (assert (local-variable-p 'allout-tests-locally-true (current-buffer)))
01fc9422
EZ
6905 (assert (equal allout-tests-locally-true 4))
6906 (allout-do-resumptions)
1c9b9df0
KM
6907 (assert (not (local-variable-p 'allout-tests-globally-unbound
6908 (current-buffer))))
01fc9422 6909 (assert (not (boundp 'allout-tests-globally-unbound)))
1c9b9df0
KM
6910 (assert (not (local-variable-p 'allout-tests-globally-true
6911 (current-buffer))))
01fc9422
EZ
6912 (assert (boundp 'allout-tests-globally-true))
6913 (assert (equal allout-tests-globally-true t))
6914 (assert (boundp 'allout-tests-locally-true))
1c9b9df0 6915 (assert (local-variable-p 'allout-tests-locally-true (current-buffer)))
01fc9422
EZ
6916 (assert (equal allout-tests-locally-true t))
6917 (assert (not (default-boundp 'allout-tests-locally-true))))
6918
6919 ;; ensure that deliberately unbinding registered variables doesn't foul things
6920 (with-temp-buffer
6921 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6922 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6923 (setq allout-tests-globally-true t)
6924 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6925 (set (make-local-variable 'allout-tests-locally-true) t)
6926 (allout-add-resumptions '(allout-tests-globally-unbound t)
6927 '(allout-tests-globally-true nil)
6928 '(allout-tests-locally-true nil))
6929 (allout-tests-obliterate-variable 'allout-tests-globally-unbound)
6930 (allout-tests-obliterate-variable 'allout-tests-globally-true)
6931 (allout-tests-obliterate-variable 'allout-tests-locally-true)
6932 (allout-do-resumptions))
6933 )
6934;;;_ % Run unit tests if `allout-run-unit-tests-after-load' is true:
6935(when allout-run-unit-tests-on-load
6936 (allout-run-unit-tests))
6937
6938;;;_ #12 Provide
96b83743 6939(provide 'allout)
1977b8f6
RS
6940
6941;;;_* Local emacs vars.
31468dcb
SM
6942;; The following `allout-layout' local variable setting:
6943;; - closes all topics from the first topic to just before the third-to-last,
6944;; - shows the children of the third to last (config vars)
6945;; - and the second to last (code section),
6946;; - and closes the last topic (this local-variables section).
6947;;Local variables:
6948;;allout-layout: (0 : -1 -1 0)
6949;;End:
6950
6a05d05f 6951;;; allout.el ends here