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