aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorPaul Eggert2011-02-17 23:44:39 -0800
committerPaul Eggert2011-02-17 23:44:39 -0800
commit37b3d30244ad822e049b6b20c2eadf5946cb02cc (patch)
tree49bfe5e475aee761975f2618be4ee1b7c8371a72 /lisp
parent0ca2f89e09202a02f392c1defba2105b69c01419 (diff)
parent7d315eb67800796d7d7f39030eb7682340d985e5 (diff)
downloademacs-37b3d30244ad822e049b6b20c2eadf5946cb02cc.tar.gz
emacs-37b3d30244ad822e049b6b20c2eadf5946cb02cc.zip
Merge from mainline.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/.gitignore2
-rw-r--r--lisp/ChangeLog126
-rw-r--r--lisp/allout-widgets.el2365
-rw-r--r--lisp/allout.el54
-rw-r--r--lisp/dired-x.el239
-rw-r--r--lisp/dired.el2
-rw-r--r--lisp/emacs-lisp/ert.el22
-rw-r--r--lisp/emacs-lisp/pcase.el9
-rw-r--r--lisp/gnus/ChangeLog78
-rw-r--r--lisp/gnus/auth-source.el332
-rw-r--r--lisp/gnus/gnus-sum.el3
-rw-r--r--lisp/gnus/gnus-util.el9
-rw-r--r--lisp/gnus/nnimap.el140
-rw-r--r--lisp/net/rcirc.el17
-rw-r--r--lisp/net/soap-client.el1741
-rw-r--r--lisp/net/soap-inspect.el357
-rw-r--r--lisp/play/doctor.el2
-rw-r--r--lisp/progmodes/prolog.el3
-rw-r--r--lisp/shell.el10
-rw-r--r--lisp/simple.el2
-rw-r--r--lisp/speedbar.el51
-rw-r--r--lisp/term.el51
-rw-r--r--lisp/term/sup-mouse.el7
-rw-r--r--lisp/term/x-win.el22
-rw-r--r--lisp/vc/vc.el3
25 files changed, 5207 insertions, 440 deletions
diff --git a/lisp/.gitignore b/lisp/.gitignore
index d8ab5055b4a..6d5166e1349 100644
--- a/lisp/.gitignore
+++ b/lisp/.gitignore
@@ -4,5 +4,3 @@ loaddefs.el
4subdirs.el 4subdirs.el
5finder-inf.el 5finder-inf.el
6cus-load.el 6cus-load.el
7
8# arch-tag: ab6e8f91-fb95-4efe-9c1b-68e21561e68a
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index a9adce5a3f5..8e850fb9409 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,129 @@
12011-02-18 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * emacs-lisp/pcase.el (pcase--u1): Understand non-linear patterns.
4
52011-02-18 Christian Ohler <ohler@gnu.org>
6
7 * emacs-lisp/ert.el (ert--setup-results-buffer)
8 (ert-results-pop-to-backtrace-for-test-at-point)
9 (ert-results-pop-to-messages-for-test-at-point)
10 (ert-results-pop-to-should-forms-for-test-at-point)
11 (ert-results-pop-to-timings): Revert parts of change 2011-02-02T17:59:44Z!sds@gnu.org that
12 were incorrect and unnecessary. This should make `make check'
13 pass again.
14
152011-02-17 Ken Manheimer <ken.manheimer@gmail.com>
16
17 * lisp/allout-widgets.el: (allout-widgets-icons-light-subdir)
18 (allout-widgets-icons-dark-subdir): Track relocations of icons
19 * lisp/allout.el: Remove commentary about remove encryption
20 passphrase mnemonic support and verification.
21 (allout-encrypt-string): (allout-encrypt-string): Recognize epg
22 failure to decrypt gpg2 armored text using gpg1, and indicate that
23 the gpg version *might* be the problem in the error message.
24
252011-02-17 Deniz Dogan <deniz.a.m.dogan@gmail.com>
26
27 * net/rcirc.el (rcirc-float-time): New function.
28 (rcirc-keepalive, rcirc-handler-ctcp-KEEPALIVE)
29 (rcirc-ctcp-sender-PING): Use it.
30
312011-02-17 Glenn Morris <rgm@gnu.org>
32
33 * speedbar.el (speedbar-ignored-modes, speedbar-file-unshown-regexp)
34 (speedbar-update-flag, speedbar-fetch-etags-command)
35 (speedbar-fetch-etags-arguments):
36 * term.el (term-buffer-maximum-size, term-input-chunk-size)
37 (term-completion-autolist, term-completion-addsuffix)
38 (term-completion-recexact, term-completion-fignore):
39 * term/sup-mouse.el (sup-mouse-fast-select-window):
40 * term/x-win.el (x-select-request-type):
41 Convert some defvars with "*" to defcustoms.
42
43 * shell.el (shell-delimiter-argument-list): Set it to nil. (Bug#8027)
44
45 * vc/vc.el (vc-default-previous-version):
46 Remove alias that points nowhere. (Bug#4496)
47
48 * dired-x.el (dired-clean-up-after-deletion):
49 kill-buffer does not need save-excursion.
50 (dired-do-run-mail): Doc fix.
51 (dired-filename-at-point): Doc fix.
52 Use looking-at, and skip-chars rather than re search.
53
54 * dired-x.el (dired-filename-at-point): Fix 8-year old typo.
55
562011-02-16 Ken Manheimer <ken.manheimer@gmail.com>
57
58 * allout-widgets.el: New allout extension that shows allout
59 outline structure with graphical widgets. 'allout-widgets'
60 customize group is an 'allout' subgroup, for easy discovery.
61
62 * allout.el: Include PGP and GnuPG in Keywords, and other
63 commentary refinements.
64 (allout-abbreviate-flattened-numbering): Rename to
65 allout-flattened-numbering-abbreviation, and
66 define-obsolete-variable-alias the old name.
67 (allout-flattened-numbering-abbreviation): Rename from
68 allout-abbreviate-flattened-numbering.
69 (allout-mode-p): Include among autoloads, for use by other modes
70 with impunity.
71 (allout-listify-exposed): Use
72 allout-flattened-numbering-abbreviation.
73 (allout-encrypt-string): Use set-buffer-multibyte directly.
74 (allout-set-buffer-multibyte): Remove.
75
762011-02-16 Deniz Dogan <deniz.a.m.dogan@gmail.com>
77
78 * simple.el (just-one-space): Remove useless `or' call.
79
802011-02-16 Alex Harsanyi <AlexHarsanyi@gmail.com>
81
82 * soap-client.el (soap-well-known-xmlns, soap-local-xmlns)
83 (soap-default-xmlns, soap-target-xmlns, soap-multi-refs)
84 (soap-decoded-multi-refs, soap-current-wsdl)
85 (soap-encoded-namespaces): Rename CL-style *...* variables.
86
872011-02-16 Michael Albinus <michael.albinus@gmx.de>
88
89 * net/soap-client.el: Add "comm" and "hypermedia" to the
90 keywords. Reflow too long lines.
91
92 * net/soap-inspect.el: Ditto. Require 'cl.
93
942011-02-16 Bastien Guerry <bzg@altern.org>
95
96 * play/doctor.el (doctor-mode): Bugfix: escape the "," character
97 in a `doctor-type' argument.
98
992011-02-16 Alex Harsanyi <AlexHarsanyi@gmail.com>
100
101 * net/soap-client.el:
102 * net/soap-inspect.el: New files.
103
1042011-02-16 Leo <sdl.web@gmail.com>
105
106 * dired-x.el (dired-mode-map, dired-extra-startup):
107 Remove dired-copy-filename-as-kill since it's already in dired.el.
108
1092011-02-16 Glenn Morris <rgm@gnu.org>
110
111 * dired-x.el (dired-bind-jump, dired-bind-man, dired-bind-info):
112 Doc fixes. Add :set property, replacing top-level calls.
113 (dired-vm-read-only-folders, dired-vm): Doc fix (drop v. old VM 4).
114 (dired-guess-shell-gnutar): Test tar version rather than system-type.
115 (dired-extra-startup, dired-man, dired-info): Doc fixes.
116 (dired-clean-up-after-deletion): Use when and dolist.
117 (dired-jump): Use unless and when.
118 (dired-virtual): Use line-end-position.
119 (dired-default-directory-alist): Rename from default-directory-alist.
120 (dired-default-directory): Update for above name change.
121 (dired-vm): Drop VM < 5 and simplify.
122 (dired-buffer-more-recently-used-p): Rewrite.
123 (dired-filename-at-point): Use when and or.
124 (dired-x-read-filename-at-point): Rename from read-filename-at-point.
125 Update callers.
126
12011-02-15 Glenn Morris <rgm@gnu.org> 1272011-02-15 Glenn Morris <rgm@gnu.org>
2 128
3 * dired-x.el: Use easymenu for menu items. Fix item capitalization. 129 * dired-x.el: Use easymenu for menu items. Fix item capitalization.
diff --git a/lisp/allout-widgets.el b/lisp/allout-widgets.el
new file mode 100644
index 00000000000..75e1e5882f6
--- /dev/null
+++ b/lisp/allout-widgets.el
@@ -0,0 +1,2365 @@
1;; allout-widgets.el --- Show allout outline structure with graphical widgets.
2
3;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Ken Manheimer
4
5;; Author: Ken Manheimer <ken dot manheimer at gmail dot com>
6;; Maintainer: Ken Manheimer <ken dot manheimer at gmail dot com>
7;; Version: 1.0
8;; Created: Dec 2005
9;; Version: 1.0
10;; Keywords: outlines
11;; Website: http://myriadicity.net/Sundry/EmacsAllout
12
13;;; Commentary:
14
15;; This is an allout outline-mode add-on that highlights outline structure
16;; with graphical widgets.
17;;
18;; To activate, customize `allout-widgets-auto-activation'. You can also
19;; invoke allout-widgets-mode in a particular allout buffer. When
20;; auto-enabled, you can inhibit widget operation in particular allout
21;; buffers by setting the variable `allout-widgets-mode-inhibit' non-nil in
22;; that file's buffer. Use emacs *file local variables* to generally
23;; inhibit for a file.
24;;
25;; See the `allout-widgets-mode' docstring for more details.
26;;
27;; Info about allout and allout-widgets development are available at
28;; http://myriadicity.net/Sundry/EmacsAllout
29;;
30;; The graphics include:
31;;
32;; - icons for item bullets, varying to distinguish whether the item either
33;; lacks any subitems, the subitems are currently collapsed within the
34;; item, or the item is currently expanded.
35;;
36;; - guide lines connecting item bullet-icons with those of their subitems.
37;;
38;; - cue area between the bullet-icon and the start of the body headline,
39;; for item numbering, encryption indicator, and distinctive bullets.
40;;
41;; The bullet-icon and guide line graphics provide keybindings and mouse
42;; bindings for easy outline navigation and exposure control, extending
43;; outline hot-spot navigation (see `allout-mode' docstring for details).
44;;
45;; Developers note: Our use of emacs widgets is unconventional. We
46;; decorate existing text rather than substituting for it, to
47;; piggy-back on existing allout operation. This employs the C-coded
48;; efficiencies of widget-apply, widget-get, and widget-put, along
49;; with the basic object-oriented organization of widget-create, to
50;; systematically couple overlays, graphics, and other features with
51;; allout-governed text.
52
53;;;_: Code (structured with comments that delinieate an allout outline)
54
55;;;_ : General Environment
56(require 'allout)
57(require 'widget)
58(require 'wid-edit)
59
60(eval-when-compile
61 (progn
62 (require 'overlay)
63 (require 'cl)
64 ))
65
66;;;_ : internal variables needed before user-customization variables
67;;; In order to enable activation of allout-widgets-mode via customization,
68;;; allout-widgets-auto-activation uses a setting function. That function
69;;; is invoked when the customization variable definition is evaluated,
70;;; during file load, so the involved code must reside above that
71;;; definition in the file.
72;;;_ = allout-widgets-mode
73(defvar allout-widgets-mode nil
74 "Allout mode enhanced with graphical widgets.")
75(make-variable-buffer-local 'allout-widgets-mode)
76
77;;;_ : USER CUSTOMIZATION VARIABLES and incidental functions:
78;;;_ > defgroup allout-widgets
79;;;###autoload
80(defgroup allout-widgets nil
81 "Allout extension that highlights outline structure graphically.
82
83Customize `allout-widgets-auto-activation' to activate allout-widgets
84with allout-mode."
85 :group 'allout)
86;;;_ > defgroup allout-widgets-developer
87(defgroup allout-widgets-developer nil
88 "Settings for development of allout widgets extension."
89 :group 'allout-widgets)
90;;;_ ; some functions a bit early, for allout-auto-activation dependency:
91;;;_ > allout-widgets-mode-enable
92(defun allout-widgets-mode-enable ()
93 "Enable allout-widgets-mode in allout-mode buffers.
94
95See `allout-widgets-mode-inhibit' for per-file/per-buffer
96inhibition of allout-widgets-mode."
97 (add-hook 'allout-mode-off-hook 'allout-widgets-mode-off)
98 (add-hook 'allout-mode-on-hook 'allout-widgets-mode-on)
99 t)
100;;;_ > allout-widgets-mode-disable
101(defun allout-widgets-mode-disable ()
102 "Disable allout-widgets-mode in allout-mode buffers.
103
104See `allout-widgets-mode-inhibit' for per-file/per-buffer
105inhibition of allout-widgets-mode."
106 (remove-hook 'allout-mode-off-hook 'allout-widgets-mode-off)
107 (remove-hook 'allout-mode-on-hook 'allout-widgets-mode-on)
108 t)
109;;;_ > allout-widgets-setup (varname value)
110;;;###autoload
111(defun allout-widgets-setup (varname value)
112 "Commission or decommision allout-widgets-mode along with allout-mode.
113
114Meant to be used by customization of `allout-widgets-auto-activation'."
115 (set-default varname value)
116 (if allout-widgets-auto-activation
117 (allout-widgets-mode-enable)
118 (allout-widgets-mode-disable)))
119;;;_ = allout-widgets-auto-activation
120;;;###autoload
121(defcustom allout-widgets-auto-activation nil
122 "Activate to enable allout icon graphics wherever allout mode is active.
123
124Also enable `allout-auto-activation' for this to take effect upon
125visiting an outline.
126
127When this is set you can disable allout widgets in select files
128by setting `allout-widgets-mode-inhibit'
129
130Instead of setting `allout-widgets-auto-activation' you can
131explicitly invoke `allout-widgets-mode' in allout buffers where
132you want allout widgets operation.
133
134See `allout-widgets-mode' for allout widgets mode features."
135 :type 'boolean
136 :group 'allout-widgets
137 :set 'allout-widgets-setup
138 )
139;; ;;;_ = allout-widgets-allow-unruly-edits
140;; (defcustom allout-widgets-allow-unruly-edits nil
141;; "*Control whether manual edits are restricted to maintain outline integrity.
142
143;; When nil, manual edits must either be within an item's body or encompass
144;; one or more items completely - eg, killing topics as entities, rather than
145;; deleting from the middle of one to the middle of another.
146
147;; If you only occasionally need to make unrestricted change, you can set this
148;; variable in the specific buffer using set-variable, or just deactivate
149;; `allout-mode' temporarily. You can customize this to always allow unruly
150;; edits, but you will be able to create outlines that are unnavigable in
151;; principle, and not just for allout's navigation and exposure mechanisms."
152;; :type 'boolean
153;; :group allout-widgets)
154;; (make-variable-buffer-local 'allout-widgets-allow-unruly-edits)
155;;;_ = allout-widgets-auto-activation - below, for eval-order dependencies
156;;;_ = allout-widgets-icons-dark-subdir
157(defcustom allout-widgets-icons-dark-subdir "icons/allout-widgets/dark-bg/"
158 "Directory on `image-load-path' holding allout icons for dark backgrounds."
159 :type 'string
160 :group 'allout-widgets)
161;;;_ = allout-widgets-icons-light-subdir
162(defcustom allout-widgets-icons-light-subdir "icons/allout-widgets/light-bg/"
163 "Directory on `image-load-path' holding allout icons for light backgrounds."
164 :type 'string
165 :group 'allout-widgets)
166;;;_ = allout-widgets-icon-types
167(defcustom allout-widgets-icon-types '(xpm png)
168 "File extensions for the icon graphic format types, in order of preference."
169 :type '(repeat symbol)
170 :group 'allout-widgets)
171
172;;;_ . Decoration format
173;;;_ = allout-widgets-theme-dark-background
174(defcustom allout-widgets-theme-dark-background "allout-dark-bg"
175 "Identify the outline's icon theme to use with a dark background."
176 :type '(string)
177 :group 'allout-widgets)
178;;;_ = allout-widgets-theme-light-background
179(defcustom allout-widgets-theme-light-background "allout-light-bg"
180 "Identify the outline's icon theme to use with a light background."
181 :type '(string)
182 :group 'allout-widgets)
183;;;_ = allout-widgets-item-image-properties-emacs
184(defcustom allout-widgets-item-image-properties-emacs
185 '(:ascent center :mask (heuristic t))
186 "*Default properties item widget images in mainline Emacs."
187 :type 'plist
188 :group 'allout-widgets)
189;;;_ = allout-widgets-item-image-properties-xemacs
190(defcustom allout-widgets-item-image-properties-xemacs
191 nil
192 "*Default properties item widget images in XEmacs."
193 :type 'plist
194 :group 'allout-widgets)
195;;;_ . Developer
196;;;_ = allout-widgets-run-unit-tests-on-load
197(defcustom allout-widgets-run-unit-tests-on-load nil
198 "*When non-nil, unit tests will be run at end of loading allout-widgets.
199
200Generally, allout widgets code developers are the only ones who'll want to
201set this.
202
203\(If set, this makes it an even better practice to exercise changes by
204doing byte-compilation with a repeat count, so the file is loaded after
205compilation.)
206
207See `allout-widgets-run-unit-tests' to see what's run."
208 :type 'boolean
209 :group 'allout-widgets-developer)
210;;;_ = allout-widgets-time-decoration-activity
211(defcustom allout-widgets-time-decoration-activity nil
212 "*Retain timing info of the last cooperative redecoration.
213
214The details are retained as the value of
215`allout-widgets-last-decoration-timing'.
216
217Generally, allout widgets code developers are the only ones who'll want to
218set this."
219 :type 'boolean
220 :group 'allout-widgets-developer)
221;;;_ = allout-widgets-hook-error-post-time 0
222(defcustom allout-widgets-hook-error-post-time 0
223 "*Amount of time to sit showing hook error messages.
224
2250 is minimal, or nil to not post to the message area.
226
227This is for debugging purposes."
228 :type 'integer
229 :group 'allout-widgets-developer)
230;;;_ = allout-widgets-maintain-tally nil
231(defcustom allout-widgets-maintain-tally nil
232 "*If non-nil, maintain a collection of widgets, `allout-widgets-tally'.
233
234This is for debugging purposes.
235
236The tally shows the total number of item widgets in the current
237buffer, and tracking increases as new widgets are added and
238decreases as obsolete widgets are garbage collected."
239 :type 'boolean
240 :group 'allout-widgets-developer)
241(defvar allout-widgets-tally nil
242 "Hash-table of existing allout widgets, for debugging.
243
244Table is maintained iff `allout-widgets-maintain-tally' is non-nil.
245
246The table contents will be out of sync if any widgets are created
247or deleted while this variable is nil.")
248(make-variable-buffer-local 'allout-widgets-tally)
249;;;_ > allout-widgets-tally-string
250(defun allout-widgets-tally-string ()
251 "Return a string giving the number of tracked widgets, or empty string if not tracking.
252
253The string is formed for appending to the allout-mode mode-line lighter.
254
255An empty string is also returned if tracking is inhibited or
256widgets are locally inhibited.
257
258The number varies according to the evanescence of objects on a
259 hash table with weak keys, so tracking of widget erasures is often delayed."
260 (when (and allout-widgets-maintain-tally (not allout-widgets-mode-inhibit))
261 (format ":%s" (hash-table-count allout-widgets-tally))))
262;;;_ = allout-widgets-track-decoration nil
263(defcustom allout-widgets-track-decoration nil
264 "*If non-nil, show cursor position of each item decoration.
265
266This is for debugging purposes, and generally set at need in a
267buffer rather than as a prevailing configuration \(but it's handy
268to publicize it by making it a customization variable\)."
269 :type 'boolean
270 :group 'allout-widgets-developer)
271(make-variable-buffer-local 'allout-widgets-track-decoration)
272
273;;;_ : Mode context - variables, hookup, and hooks
274;;;_ . internal mode variables
275;;;_ , Mode activation and environment
276;;;_ = allout-widgets-version
277(defvar allout-widgets-version "1.0"
278 "Version of currently loaded allout-widgets extension.")
279;;;_ > allout-widgets-version
280(defun allout-widgets-version (&optional here)
281 "Return string describing the loaded outline version."
282 (interactive "P")
283 (let ((msg (concat "Allout Outline Widgets Extension v "
284 allout-widgets-version)))
285 (if here (insert msg))
286 (message "%s" msg)
287 msg))
288;;;_ = allout-widgets-mode-inhibit
289(defvar allout-widgets-mode-inhibit nil
290 "Inhibit `allout-widgets-mode' from activating widgets.
291
292This also inhibits automatic adjustment of widgets to track allout outline
293changes.
294
295You can use this as a file local variable setting to disable
296allout widgets enhancements in selected buffers while generally
297enabling widgets by customizing `allout-widgets-auto-activation'.
298
299In addition, you can invoked `allout-widgets-mode' allout-mode
300buffers where this is set to enable and disable widget
301enhancements, directly.")
302;;;###autoload
303(put 'allout-widgets-mode-inhibit 'safe-local-variable
304 (if (fboundp 'booleanp) 'booleanp '(lambda (x) (member x '(t nil)))))
305(make-variable-buffer-local 'allout-widgets-mode-inhibit)
306;;;_ = allout-inhibit-body-modification-hook
307(defvar allout-inhibit-body-modification-hook nil
308 "Override de-escaping of text-prefixes in item bodies during specific changes.
309
310This is used by `allout-buffer-modification-handler' to signal such changes
311to `allout-body-modification-handler', and is always reset by
312`allout-post-command-business'.")
313(make-variable-buffer-local 'allout-inhibit-body-modification-hook)
314;;;_ = allout-widgets-icons-cache
315(defvar allout-widgets-icons-cache nil
316 "Cache allout icon images, as an association list.
317
318`allout-fetch-icon-image' uses this cache transparently, keying
319images with lists containing the name of the icon directory \(as
320found on the `load-path') and the icon name.
321
322Set this variable to `nil' to empty the cache, and have it replenish from the
323filesystem.")
324;;;_ = allout-widgets-unset-inhibit-read-only
325(defvar allout-widgets-unset-inhibit-read-only nil
326 "Tell `allout-widgets-post-command-business' to unset `inhibit-read-only'.
327
328Used by `allout-graphics-modification-handler'")
329;;;_ = allout-widgets-reenable-before-change-handler
330(defvar allout-widgets-reenable-before-change-handler nil
331 "Tell `allout-widgets-post-command-business' to reequip the handler.
332
333Necessary because the handler sometimes deliberately raises an
334error, causing it to be disabled.")
335;;;_ , State for hooks
336;;;_ = allout-unresolved-body-mod-workroster
337(defvar allout-unresolved-body-mod-workroster (make-hash-table :size 16)
338 "List of body-overlays that did before-change business but not after-change.
339
340See `allout-post-command-business' and `allout-body-modification-handler'.")
341;;;_ = allout-structure-unruly-deletion-message
342(defvar allout-structure-unruly-deletion-message
343 "Unruly edit prevented --
344To change the bullet character: \\[allout-rebullet-current-heading]
345To promote this item: \\[allout-shift-out]
346To demote it: \\[allout-shift-in]
347To delete it and offspring: \\[allout-kill-topic]
348See \\[describe-mode] for many more options."
349 "Informative message presented on improper editing of outline structure.
350
351The structure includes the guides lines, bullet, and bullet cue.")
352;;;_ = allout-widgets-changes-record
353(defvar allout-widgets-changes-record nil
354 "Record outline changes for processing by post-command hook.
355
356Entries on the list are lists whose first element is a symbol indicating
357the change type and subsequent elements are data specific to that change
358type. Specifically:
359
360 'exposure `allout-exposure-from' `allout-exposure-to' `allout-exposure-flag'
361
362The changes are recorded in reverse order, with new values pushed
363onto the front.")
364(make-variable-buffer-local 'allout-widgets-changes-record)
365;;;_ = allout-widgets-undo-exposure-record
366(defvar allout-widgets-undo-exposure-record nil
367 "Record outline undo traces for processing by post-command hook.
368
369The changes are recorded in reverse order, with new values pushed
370onto the front.")
371(make-variable-buffer-local 'allout-widgets-undo-exposure-record)
372;;;_ = allout-widgets-last-hook-error
373(defvar allout-widgets-last-hook-error nil
374 "String holding last error string, for debugging purposes.")
375;;;_ = allout-widgets-adjust-message-length-threshold 100
376(defvar allout-widgets-adjust-message-length-threshold 100
377 "Display \"Adjusting widgets\" message above this number of pending changes."
378 )
379;;;_ = allout-widgets-adjust-message-size-threshold 10000
380(defvar allout-widgets-adjust-message-size-threshold 10000
381 "Display \"Adjusting widgets\" message above this size of pending changes."
382 )
383;;;_ = allout-doing-exposure-undo-processor nil
384(defvar allout-undo-exposure-in-progress nil
385 "Maintained true during `allout-widgets-exposure-undo-processor'")
386;;;_ , Widget-specific outline text format
387;;;_ = allout-escaped-prefix-regexp
388(defvar allout-escaped-prefix-regexp ""
389 "*Regular expression for body text that would look like an item prefix if
390not altered with an escape sequence.")
391(make-variable-buffer-local 'allout-escaped-prefix-regexp)
392;;;_ , Widget element formatting
393;;;_ = allout-item-icon-keymap
394(defvar allout-item-icon-keymap
395 (let ((km (make-sparse-keymap)))
396 (dolist (digit '("0" "1" "2" "3"
397 "4" "5" "6" "7" "8" "9"))
398 (define-key km digit 'digit-argument))
399 (define-key km "-" 'negative-argument)
400;; (define-key km [(return)] 'allout-tree-expand-command)
401;; (define-key km [(meta return)] 'allout-toggle-torso-command)
402;; (define-key km [(down-mouse-1)] 'allout-item-button-click)
403;; (define-key km [(down-mouse-2)] 'allout-toggle-torso-event-command)
404 ;; Override underlying mouse-1 and mouse-2 bindings in icon territory:
405 (define-key km [(mouse-1)] (lambda () (interactive) nil))
406 (define-key km [(mouse-2)] (lambda () (interactive) nil))
407
408 ;; Catchall, handles actual keybindings, dynamically doing keymap lookups:
409 (define-key km [t] 'allout-item-icon-key-handler)
410
411 km)
412 "General tree-node key bindings.")
413;;;_ = allout-item-body-keymap
414(defvar allout-item-body-keymap
415 (let ((km (make-sparse-keymap))
416 (local-map (current-local-map)))
417;; (define-key km [(control return)] 'allout-tree-expand-command)
418;; (define-key km [(meta return)] 'allout-toggle-torso-command)
419 ;; XXX We need to reset this per buffer's mode; we do so in
420 ;; allout-widgets-mode.
421 (if local-map
422 (set-keymap-parent km local-map))
423
424 km)
425 "General key bindings for the text content of outline items.")
426(make-variable-buffer-local 'allout-item-body-keymap)
427;;;_ = allout-body-span-category
428(defvar allout-body-span-category nil
429 "Symbol carrying allout body-text overlay properties.")
430;;;_ = allout-cue-span-keymap
431(defvar allout-cue-span-keymap
432 (let ((km (make-sparse-keymap)))
433 (set-keymap-parent km allout-item-icon-keymap)
434 km)
435 "Keymap used in the item cue area - the space between the icon and headline.")
436;;;_ = allout-escapes-category
437(defvar allout-escapes-category nil
438 "Symbol for category of text property used to hide escapes of prefix-like
439text in allout item bodies.")
440;;;_ = allout-guides-category
441(defvar allout-guides-category nil
442 "Symbol carrying allout icon-guides overlay properties.")
443;;;_ = allout-guides-span-category
444(defvar allout-guides-span-category nil
445 "Symbol carrying allout icon and guide lines overlay properties.")
446;;;_ = allout-icon-span-category
447(defvar allout-icon-span-category nil
448 "Symbol carrying allout icon and guide lines overlay properties.")
449;;;_ = allout-cue-span-category
450(defvar allout-cue-span-category nil
451 "Symbol carrying common properties of the space following the outline icon.
452
453\(That space is used to convey selected cues indicating body qualities,
454including things like:
455 - encryption '~'
456 - numbering '#'
457 - indirect reference '@'
458 - distinctive bullets - see `allout-distinctive-bullets-string'.\)")
459;;;_ = allout-span-to-category
460(defvar allout-span-to-category
461 '((:guides-span . allout-guides-span-category)
462 (:cue-span . allout-cue-span-category)
463 (:icon-span . allout-icon-span-category)
464 (:body-span . allout-body-span-category))
465 "Association list mapping span identifier to category identifier.")
466;;;_ = allout-trailing-category
467(defvar allout-trailing-category nil
468 "Symbol carrying common properties of an overlay's trailing newline.")
469;;;_ , Developer
470(defvar allout-widgets-last-decoration-timing nil
471 "Timing details for the last cooperative decoration action.
472
473This is maintained when `allout-widgets-time-decoration-activity' is set.
474
475The value is a list containing two elements:
476 - the elapsed time as a number of seconds
477 - the list of changes processed, a la `allout-widgets-changes-record'.
478
479When active, the value is revised each time automatic decoration activity
480happens in the buffer.")
481(make-variable-buffer-local 'allout-widgets-last-decoration-timing)
482;;;_ . mode hookup
483;;;_ > define-minor-mode allout-widgets-mode (arg)
484;;;###autoload
485(define-minor-mode allout-widgets-mode
486 "Allout-mode extension, providing graphical decoration of outline structure.
487
488This is meant to operate along with allout-mode, via `allout-mode-hook'.
489
490If optional argument ARG is greater than 0, enable.
491If optional argument ARG is less than 0, disable.
492Anything else, toggle between active and inactive.
493
494The graphics include:
495
496- guide lines connecting item bullet-icons with those of their subitems.
497
498- icons for item bullets, varying to indicate whether or not the item
499 has subitems, and if so, whether or not the item is expanded.
500
501- cue area between the bullet-icon and the start of the body headline,
502 for item numbering, encryption indicator, and distinctive bullets.
503
504The bullet-icon and guide line graphics provide keybindings and mouse
505bindings for easy outline navigation and exposure control, extending
506outline hot-spot navigation \(see `allout-mode')."
507
508 :lighter nil
509 :keymap nil
510
511 ;; define-minor-mode handles any provided argument according to emacs
512 ;; minor-mode conventions - '(elisp) Minor Mode Conventions' - and sets
513 ;; allout-widgets-mode accordingly *before* running the body code, so we
514 ;; cue on that.
515 (if allout-widgets-mode
516 ;; Activating:
517 (progn
518 (allout-add-resumptions
519 ;; XXX user may need say in line-truncation/hscrolling - an option
520 ;; that abstracts mode.
521 ;; truncate text lines to keep guide lines intact:
522 '(truncate-lines t)
523 ;; and enable autoscrolling to ease view of text
524 '(auto-hscroll-mode t)
525 '(line-move-ignore-fields t)
526 '(widget-push-button-prefix "")
527 '(widget-push-button-suffix "")
528 ;; allout-escaped-prefix-regexp depends on allout-regexp:
529 (list 'allout-escaped-prefix-regexp (concat "\\(\\\\\\)"
530 "\\(" allout-regexp "\\)")))
531 (allout-add-resumptions
532 (list 'allout-widgets-tally allout-widgets-tally)
533 (list 'allout-widgets-escapes-sanitization-regexp-pair
534 (list (concat "\\(\n\\|\\`\\)"
535 allout-escaped-prefix-regexp
536 )
537 ;; Include everything but the escape symbol.
538 "\\1\\3"))
539 )
540
541 (add-hook 'after-change-functions 'allout-widgets-after-change-handler
542 nil t)
543
544 (allout-setup-text-properties)
545 (add-to-invisibility-spec '(allout-torso . t))
546 (add-to-invisibility-spec 'allout-escapes)
547
548 (if (current-local-map)
549 (set-keymap-parent allout-item-body-keymap (current-local-map)))
550
551 (add-hook 'allout-exposure-change-hook
552 'allout-widgets-exposure-change-recorder nil 'local)
553 (add-hook 'allout-structure-added-hook
554 'allout-widgets-additions-recorder nil 'local)
555 (add-hook 'allout-structure-deleted-hook
556 'allout-widgets-deletions-recorder nil 'local)
557 (add-hook 'allout-structure-shifted-hook
558 'allout-widgets-shifts-recorder nil 'local)
559 (add-hook 'allout-after-copy-or-kill-hook
560 'allout-widgets-after-copy-or-kill-function nil 'local)
561
562 (add-hook 'before-change-functions 'allout-widgets-before-change-handler
563 nil 'local)
564 (add-hook 'post-command-hook 'allout-widgets-post-command-business
565 nil 'local)
566 (add-hook 'pre-command-hook 'allout-widgets-pre-command-business
567 nil 'local)
568
569 ;; init the widgets tally for debugging:
570 (if (not allout-widgets-tally)
571 (setq allout-widgets-tally (make-hash-table
572 :test 'eq :weakness 'key)))
573 ;; add tally count display on minor-mode-alist just after
574 ;; allout-mode entry.
575 ;; (we use ternary condition form to keep condition simple for deletion.)
576 (let* ((mode-line-entry '(allout-widgets-mode-inhibit ""
577 (:eval (allout-widgets-tally-string))))
578 (associated (assoc (car mode-line-entry) minor-mode-alist))
579 ;; need location for it only if not already present:
580 (after (and (not associated)
581 (memq (assq 'allout-mode minor-mode-alist) minor-mode-alist))))
582 (if after
583 (rplacd after (cons mode-line-entry (cdr after)))))
584 (allout-widgets-prepopulate-buffer)
585 t)
586 ;; Deactivating:
587 (let ((inhibit-read-only t)
588 (was-modified (buffer-modified-p)))
589
590 (allout-widgets-undecorate-region (point-min)(point-max))
591 (remove-from-invisibility-spec '(allout-torso . t))
592 (remove-from-invisibility-spec 'allout-escapes)
593
594 (remove-hook 'after-change-functions
595 'allout-widgets-after-change-handler 'local)
596 (remove-hook 'allout-exposure-change-hook
597 'allout-widgets-exposure-change-recorder 'local)
598 (remove-hook 'allout-structure-added-hook
599 'allout-widgets-additions-recorder 'local)
600 (remove-hook 'allout-structure-deleted-hook
601 'allout-widgets-deletions-recorder 'local)
602 (remove-hook 'allout-structure-shifted-hook
603 'allout-widgets-shifts-recorder 'local)
604 (remove-hook 'allout-after-copy-or-kill-hook
605 'allout-widgets-after-copy-or-kill-function 'local)
606 (remove-hook 'before-change-functions
607 'allout-widgets-before-change-handler 'local)
608 (remove-hook 'post-command-hook
609 'allout-widgets-post-command-business 'local)
610 (remove-hook 'pre-command-hook
611 'allout-widgets-pre-command-business 'local)
612 (assq-delete-all 'allout-widgets-mode-inhibit minor-mode-alist)
613 (set-buffer-modified-p was-modified))))
614;;;_ > allout-widgets-mode-off
615(defun allout-widgets-mode-off ()
616 "Explicitly disable allout-widgets-mode."
617 (allout-widgets-mode -1))
618;;;_ > allout-widgets-mode-off
619(defun allout-widgets-mode-on ()
620 "Explicitly disable allout-widgets-mode."
621 (allout-widgets-mode 1))
622;;;_ > allout-setup-text-properties ()
623(defun allout-setup-text-properties ()
624 "Configure category and literal text properties."
625
626 ;; XXX body - before-change, entry, keymap
627
628 (setplist 'allout-guides-span-category nil)
629 (put 'allout-guides-span-category
630 'modification-hooks '(allout-graphics-modification-handler))
631 (put 'allout-guides-span-category 'local-map allout-item-icon-keymap)
632 (put 'allout-guides-span-category 'mouse-face widget-button-face)
633 (put 'allout-guides-span-category 'field 'structure)
634;; (put 'allout-guides-span-category 'face 'widget-button)
635
636 (setplist 'allout-icon-span-category
637 (allout-widgets-copy-list (symbol-plist
638 'allout-guides-span-category)))
639 (put 'allout-icon-span-category 'field 'structure)
640
641 ;; XXX for body text we're instead going to use the buffer-wide
642 ;; resources, like before/after-change-functions hooks and the
643 ;; buffer's key map. that way we won't have to do painful provisions
644 ;; to fixup things after edits, catch outlier interstitial
645 ;; characters, like newline and empty lines after hidden subitems,
646 ;; etc.
647 (setplist 'allout-body-span-category nil)
648 (put 'allout-body-span-category 'evaporate t)
649 (put 'allout-body-span-category 'local-map allout-item-body-keymap)
650 ;;(put 'allout-body-span-category
651 ;; 'modification-hooks '(allout-body-modification-handler))
652 ;;(put 'allout-body-span-category 'field 'body)
653
654 (setplist 'allout-cue-span-category nil)
655 (put 'allout-cue-span-category 'evaporate t)
656 (put 'allout-cue-span-category
657 'modification-hooks '(allout-body-modification-handler))
658 (put 'allout-cue-span-category 'local-map allout-cue-span-keymap)
659 (put 'allout-cue-span-category 'mouse-face widget-button-face)
660 (put 'allout-cue-span-category 'pointer 'arrow)
661 (put 'allout-cue-span-category 'field 'structure)
662
663 (setplist 'allout-trailing-category nil)
664 (put 'allout-trailing-category 'evaporate t)
665 (put 'allout-trailing-category 'local-map allout-item-body-keymap)
666
667 (setplist 'allout-escapes-category nil)
668 (put 'allout-escapes-category 'invisible 'allout-escapes)
669 (put 'allout-escapes-category 'evaporate t))
670;;;_ > allout-widgets-prepopulate-buffer ()
671(defun allout-widgets-prepopulate-buffer ()
672 "Step over the current buffers exposed items to do initial widgetizing."
673 (if (not allout-widgets-mode-inhibit)
674 (save-excursion
675 (goto-char (point-min))
676 (while (allout-next-visible-heading 1)
677 (when (not (widget-at (point)))
678 (allout-get-or-create-item-widget))))))
679;;;_ . settings context
680;;;_ = allout-container-item
681(defvar allout-container-item-widget nil
682 "A widget for the current outline's overarching container as an item.
683
684The item has settings \(of the file/connection\) and maybe a body, but no
685icon/bullet.")
686(make-variable-buffer-local 'allout-container-item-widget)
687;;;_ . Hooks and hook helpers
688;;;_ , major command-loop business:
689;;;_ > allout-widgets-pre-command-business (&optional recursing)
690(defun allout-widgets-pre-command-business (&optional recursing)
691 "Handle actions pending before allout-mode activity."
692)
693;;;_ > allout-widgets-post-command-business (&optional recursing)
694(defun allout-widgets-post-command-business (&optional recursing)
695 "Handle actions pending after any allout-mode commands.
696
697Optional RECURSING is for internal use, to limit recursion."
698 ;; - check changed text for nesting discontinuities and escape anything
699 ;; that's: (1) asterisks at bol or (2) excessively nested.
700 (condition-case failure
701
702 (when (and (boundp 'allout-mode) allout-mode)
703
704 (if allout-widgets-unset-inhibit-read-only
705 (setq inhibit-read-only nil
706 allout-widgets-unset-inhibit-read-only nil))
707
708 (when allout-widgets-reenable-before-change-handler
709 (add-hook 'before-change-functions
710 'allout-widgets-before-change-handler
711 nil 'local)
712 (setq allout-widgets-reenable-before-change-handler nil))
713
714 (when (or allout-widgets-undo-exposure-record
715 allout-widgets-changes-record)
716 (let* ((debug-on-signal t)
717 (debug-on-error t)
718 ;; inhibit recording new undo records when processing
719 ;; effects of undo-exposure:
720 (debugger 'allout-widgets-hook-error-handler)
721 (adjusting-message " Adjusting widgets...")
722 (replaced-message (allout-widgets-adjusting-message
723 adjusting-message))
724 (start-time (current-time)))
725
726 (if allout-widgets-undo-exposure-record
727 ;; inhibit undo recording iff undoing exposure stuff.
728 ;; XXX we might need to inhibit per respective
729 ;; change-record, rather than assuming that some undo
730 ;; activity during a command is all undo activity.
731 (let ((buffer-undo-list t))
732 (allout-widgets-exposure-undo-processor)
733 (allout-widgets-changes-dispatcher))
734 (allout-widgets-exposure-undo-processor)
735 (allout-widgets-changes-dispatcher))
736
737 (if allout-widgets-time-decoration-activity
738 (setq allout-widgets-last-decoration-timing
739 (list (allout-elapsed-time-seconds (current-time)
740 start-time)
741 allout-widgets-changes-record)))
742
743 (setq allout-widgets-changes-record nil)
744
745 (if replaced-message
746 (if (stringp replaced-message)
747 (message replaced-message)
748 (message "")))))
749
750 ;; Detect undecorated items, eg during isearch into previously
751 ;; unexposed topics, and decorate "economically". Some
752 ;; undecorated stuff is often exposed, to reduce lag, but the
753 ;; item containing the cursor is decorated. We constrain
754 ;; recursion to avoid being trapped by unexpectedly undecoratable
755 ;; items.
756 (when (and (not recursing)
757 (not (allout-current-decorated-p))
758 (or (not (equal (allout-depth) 0))
759 (not allout-container-item-widget)))
760 (let ((buffer-undo-list t))
761 (allout-widgets-exposure-change-recorder
762 allout-recent-prefix-beginning allout-recent-prefix-end nil)
763 (allout-widgets-post-command-business 'recursing)))
764
765 ;; Detect and rectify fouled outline structure - decorated item
766 ;; not at beginning of line.
767 (let ((this-widget (or (widget-at (point))
768 ;; XXX we really should be checking across
769 ;; edited span, not just point and point+1
770 (and (not (eq (point) (point-max)))
771 (widget-at (1+ (point))))))
772 inserted-at)
773 (save-excursion
774 (if (and this-widget
775 (goto-char (widget-get this-widget :from))
776 (not (bolp)))
777 (if (not
778 (condition-case err
779 (yes-or-no-p
780 (concat "Misplaced item won't be recognizable "
781 " as part of outline - rectify? "))
782 (quit nil)))
783 (progn
784 (if (allout-hidden-p (max (1- (point)) 1))
785 (save-excursion
786 (goto-char (max (1- (point)) 1))
787 (allout-show-to-offshoot)))
788 (allout-widgets-undecorate-item this-widget))
789 ;; expose any hidden intervening items, so resulting
790 ;; position is clear:
791 (setq inserted-at (point))
792 (allout-unprotected (insert-before-markers "\n"))
793 (forward-char -1)
794 ;; ensure the inserted newline is visible:
795 (allout-flag-region inserted-at (1+ inserted-at) nil)
796 (allout-widgets-post-command-business 'recursing)
797 (message (concat "outline structure corrected - item"
798 " moved to beginning of new line"))
799 ;; preserve cursor position in some cases:
800 (if (and inserted-at
801 (> (point) inserted-at))
802 (forward-char -1)))))))
803
804 (error
805 ;; zero work list so we don't get stuck futily retrying.
806 ;; error recording done by allout-widgets-hook-error-handler.
807 (setq allout-widgets-changes-record nil))))
808;;;_ , major change handlers:
809;;;_ > allout-widgets-before-change-handler
810(defun allout-widgets-before-change-handler (beg end)
811 "Business to be done before changes in a widgetized allout outline."
812 ;; protect against unruly edits to structure:
813 (cond
814 (undo-in-progress (when (eq (get-text-property beg 'category)
815 'allout-icon-span-category)
816 (save-excursion
817 (goto-char beg)
818 (let* ((item-widget (allout-get-item-widget)))
819 (if item-widget
820 (allout-widgets-exposure-undo-recorder
821 item-widget))))))
822 (inhibit-read-only t)
823 ((not (and (boundp 'allout-mode) allout-mode)) t)
824 ((equal this-command 'quoted-insert) t)
825 ((not (text-property-any beg (if (equal end beg)
826 (min (1+ beg) (point-max))
827 end)
828 'field 'structure))
829 t)
830 ((yes-or-no-p "Unruly edit of outline structure - allow? ")
831 (setq allout-widgets-unset-inhibit-read-only (not inhibit-read-only)
832 inhibit-read-only t))
833 (t
834 ;; tell the allout-widgets-post-command-business to reestablish the hook:
835 (setq allout-widgets-reenable-before-change-handler t)
836 ;; and raise an error to prevent the edit (and disable the hook):
837 (error
838 (substitute-command-keys allout-structure-unruly-deletion-message)))))
839;;;_ > allout-widgets-after-change-handler
840(defun allout-widgets-after-change-handler (beg end prelength)
841 "Reconcile what needs to be reconciled for allout widgets after edits."
842 )
843;;;_ > allout-current-decorated-p ()
844(defun allout-current-decorated-p ()
845 "True if the current item is not decorated"
846 (save-excursion
847 (if (allout-back-to-current-heading)
848 (if (> allout-recent-depth 0)
849 (and (allout-get-item-widget) t)
850 allout-container-item-widget))))
851
852;;;_ > allout-widgets-hook-error-handler
853(defun allout-widgets-hook-error-handler (mode args)
854 "Process errors which occurred in the course of command hook operation.
855
856We store a backtrace of the error information in the variable,
857`allout-widgets-last-hook-error', unset the error handlers, and
858reraise the error, so that processing continues to the
859encompassing condition-case."
860 ;; first deconstruct special error environment so errors here propagate
861 ;; to encompassing condition-case:
862 (setq debugger 'debug
863 debug-on-error nil
864 debug-on-signal nil)
865 (let* ((bt (with-output-to-string (backtrace)))
866 (this "allout-widgets-hook-error-handler")
867 (header
868 (format "allout-widgets-last-hook-error stored, %s/%s %s %s"
869 this mode args
870 (format-time-string "%e-%b-%Y %r" (current-time)))))
871 ;; post to *Messages* then immediately replace with more compact notice:
872 (message "%s" (setq allout-widgets-last-hook-error
873 (format "%s:\n%s" header bt)))
874 (message header) (sit-for allout-widgets-hook-error-post-time)
875 ;; reraise the error, or one concerning this function if unexpected:
876 (if (equal mode 'error)
877 (apply 'signal args)
878 (error "%s: unexpected mode, %s %s" this mode args))))
879;;;_ > allout-widgets-changes-exceed-threshold-p ()
880(defun allout-widgets-adjusting-message (message)
881 "Post MESSAGE when pending are likely to make a big enough delay.
882
883If posting of the MESSAGE is warranted and there already is a
884`current-message' in the minibuffer, the MESSAGE is appended to
885the current one, and the previously pending `current-message' is
886returned for later posting on completion.
887
888If posting of the MESSAGE is warranted, but no `current-message'
889is pending, then t is returned to indicate that case.
890
891If posting of the MESSAGE is not warranted, then nil is returned.
892
893See `allout-widgets-adjust-message-length-threshold',
894`allout-widgets-adjust-message-size-threshold' for message
895posting threshold criteria."
896 (if (or (> (length allout-widgets-changes-record)
897 allout-widgets-adjust-message-length-threshold)
898 ;; for size, use distance from start of first to end of last:
899 (let ((min (point-max))
900 (max 0)
901 first second)
902 (mapc (function (lambda (entry)
903 (if (eq :undone-exposure (car entry))
904 nil
905 (setq first (cadr entry)
906 second (caddr entry))
907 (if (< (min first second) min)
908 (setq min (min first second)))
909 (if (> (max first second) max)
910 (setq max (max first second))))))
911 allout-widgets-changes-record)
912 (> (- max min) allout-widgets-adjust-message-size-threshold)))
913 (let ((prior (current-message)))
914 (message (if prior (concat prior " - " message) message))
915 (or prior t))))
916;;;_ > allout-widgets-changes-dispatcher ()
917(defun allout-widgets-changes-dispatcher ()
918 "Dispatch CHANGES-RECORD items to respective widgets change processors."
919
920 (if (not allout-widgets-mode-inhibit)
921 (let* ((changes-record allout-widgets-changes-record)
922 (changes-pending (and changes-record t))
923 entry
924 exposures
925 additions
926 deletions
927 shifts)
928
929 (when changes-pending
930 (while changes-record
931 (setq entry (pop changes-record))
932 (case (car entry)
933 (:exposed (push entry exposures))
934 (:added (push entry additions))
935 (:deleted (push entry deletions))
936 (:shifted (push entry shifts))))
937
938 (if exposures
939 (allout-widgets-exposure-change-processor exposures))
940 (if additions
941 (allout-widgets-additions-processor additions))
942 (if deletions
943 (allout-widgets-deletions-processor deletions))
944 (if shifts
945 (allout-widgets-shifts-processor shifts))))
946 (when (not (equal allout-widgets-mode-inhibit 'undecorated))
947 (allout-widgets-undecorate-region (point-min)(point-max))
948 (setq allout-widgets-mode-inhibit 'undecorated))))
949;;;_ > allout-widgets-exposure-change-recorder (from to flag)
950(defun allout-widgets-exposure-change-recorder (from to flag)
951 "Record allout exposure changes for tracking during post-command processing.
952
953Records changes in `allout-widgets-changes-record'."
954 (push (list :exposed from to flag) allout-widgets-changes-record))
955;;;_ > allout-widgets-exposure-change-processor (changes)
956(defun allout-widgets-exposure-change-processor (changes)
957 "Widgetize and adjust item widgets tracking allout outline exposure changes.
958
959Generally invoked via `allout-exposure-change-hook'."
960
961 (let ((changes (sort changes (function (lambda (this next)
962 (< (cadr this) (cadr next))))))
963 ;; have to distinguish between concealing and exposing so that, eg,
964 ;; `allout-expose-topic's mix is handled properly.
965 handled-expose
966 handled-conceal
967 covered
968 deactivate-mark)
969
970 (dolist (change changes)
971 (let (handling
972 (from (cadr change))
973 bucket got
974 (to (caddr change))
975 (flag (cadddr change))
976 parent)
977
978 ;; swap from and to:
979 (if (< to from) (setq bucket to
980 to from
981 from bucket))
982
983 ;; have we already handled exposure changes in this region?
984 (setq handling (if flag 'handled-conceal 'handled-expose)
985 got (allout-range-overlaps from to (symbol-value handling))
986 covered (car got))
987 (set handling (cadr got))
988
989 (when (not covered)
990 (save-excursion
991 (goto-char from)
992 (cond
993
994 ;; collapsing:
995 (flag
996 (allout-widgets-undecorate-region from to)
997 (allout-beginning-of-current-line)
998 (let ((widget (allout-get-item-widget)))
999 (if (not widget)
1000 (allout-get-or-create-item-widget)
1001 (widget-apply widget :redecorate))))
1002
1003 ;; expanding:
1004 (t
1005 (while (< (point) to)
1006 (allout-beginning-of-current-line)
1007 (setq parent (allout-get-item-widget))
1008 (if (not parent)
1009 (setq parent (allout-get-or-create-item-widget))
1010 (widget-apply parent :redecorate))
1011 (allout-next-visible-heading 1)
1012 (if (widget-get parent :has-subitems)
1013 (allout-redecorate-visible-subtree parent))
1014 (if (> (point) to)
1015 ;; subtree may be well beyond to - incorporate in ranges:
1016 (setq handled-expose
1017 (allout-range-overlaps from (point) handled-expose)
1018 covered (car handled-expose)
1019 handled-expose (cadr handled-expose)))
1020 (allout-next-visible-heading 1))))))))))
1021
1022;;;_ > allout-widgets-additions-recorder (from to)
1023(defun allout-widgets-additions-recorder (from to)
1024 "Record allout item additions for tracking during post-command processing.
1025
1026Intended for use on `allout-structure-added-hook'.
1027
1028FROM point at the start of the first new item and TO is point at the start
1029of the last one.
1030
1031Records changes in `allout-widgets-changes-record'."
1032 (push (list :added from to) allout-widgets-changes-record))
1033;;;_ > allout-widgets-additions-processor (changes)
1034(defun allout-widgets-additions-processor (changes)
1035 "Widgetize and adjust items tracking allout outline structure additions.
1036
1037Dispatched by `allout-widgets-post-command-business' in response to
1038:added entries recorded by `allout-widgets-additions-recorder'."
1039 (save-excursion
1040 (let (handled
1041 covered)
1042 (dolist (change changes)
1043 (let ((from (cadr change))
1044 bucket
1045 (to (caddr change)))
1046 (if (< to from) (setq bucket to to from from bucket))
1047 ;; have we already handled exposure changes in this region?
1048 (setq handled (allout-range-overlaps from to handled)
1049 covered (car handled)
1050 handled (cadr handled))
1051 (when (not covered)
1052 (goto-char from)
1053 ;; Prior sibling and parent can both be affected.
1054 (if (allout-ascend)
1055 (allout-redecorate-visible-subtree
1056 (allout-get-or-create-item-widget 'redecorate)))
1057 (if (< (point) from)
1058 (goto-char from))
1059 (while (and (< (point) to) (not (eobp)))
1060 (allout-beginning-of-current-line)
1061 (allout-redecorate-visible-subtree
1062 (allout-get-or-create-item-widget))
1063 (allout-next-visible-heading 1))
1064 (if (> (point) to)
1065 ;; subtree may be well beyond to - incorporate in ranges:
1066 (setq handled (allout-range-overlaps from (point) handled)
1067 covered (car handled)
1068 handled (cadr handled)))))))))
1069
1070;;;_ > allout-widgets-deletions-recorder (depth from)
1071(defun allout-widgets-deletions-recorder (depth from)
1072 "Record allout item deletions for tracking during post-command processing.
1073
1074Intended for use on `allout-structure-deleted-hook'.
1075
1076DEPTH is the depth of the deleted subtree, and FROM is the point from which
1077the subtree was deleted.
1078
1079Records changes in `allout-widgets-changes-record'."
1080 (push (list :deleted depth from) allout-widgets-changes-record))
1081;;;_ > allout-widgets-deletions-processor (changes)
1082(defun allout-widgets-deletions-processor (changes)
1083 "Adjust items tracking allout outline structure deletions.
1084
1085Dispatched by `allout-widgets-post-command-business' in response to
1086:deleted entries recorded by `allout-widgets-deletions-recorder'."
1087 (save-excursion
1088 (dolist (change changes)
1089 (let ((depth (cadr change))
1090 (from (caddr change)))
1091 (goto-char from)
1092 (when (allout-previous-visible-heading 1)
1093 (if (> depth 1)
1094 (allout-ascend-to-depth (1- depth)))
1095 (allout-redecorate-visible-subtree
1096 (allout-get-or-create-item-widget 'redecorate)))))))
1097
1098;;;_ > allout-widgets-shifts-recorder (shifted-amount at)
1099(defun allout-widgets-shifts-recorder (shifted-amount at)
1100 "Record outline subtree shifts for tracking during post-command processing.
1101
1102Intended for use on `allout-structure-shifted-hook'.
1103
1104SHIFTED-AMOUNT is the depth change and AT is the point at the start of the
1105subtree that's been shifted.
1106
1107Records changes in `allout-widgets-changes-record'."
1108 (push (list :shifted shifted-amount at) allout-widgets-changes-record))
1109;;;_ > allout-widgets-shifts-processor (changes)
1110(defun allout-widgets-shifts-processor (changes)
1111 "Widgetize and adjust items tracking allout outline structure additions.
1112
1113Dispatched by `allout-widgets-post-command-business' in response to
1114:shifted entries recorded by `allout-widgets-shifts-recorder'."
1115 (save-excursion
1116 (dolist (change changes)
1117 (goto-char (caddr change))
1118 (allout-ascend)
1119 (allout-redecorate-visible-subtree))))
1120;;;_ > allout-widgets-after-copy-or-kill-function ()
1121(defun allout-widgets-after-copy-or-kill-function ()
1122 "Do allout-widgets processing of text just placed in the kill ring.
1123
1124Intended for use on allout-after-copy-or-kill-hook."
1125 (if (car kill-ring)
1126 (setcar kill-ring (allout-widgets-undecorate-text (car kill-ring)))))
1127
1128;;;_ > allout-widgets-exposure-undo-recorder (widget from-state)
1129(defun allout-widgets-exposure-undo-recorder (widget)
1130 "Record outline exposure undo for tracking during post-command processing.
1131
1132Intended for use by `allout-graphics-modification-handler'.
1133
1134WIDGET is the widget being changed.
1135
1136Records changes in `allout-widgets-changes-record'."
1137 ;; disregard the events if we're currently processing them.
1138 (if (not allout-undo-exposure-in-progress)
1139 (push widget allout-widgets-undo-exposure-record)))
1140;;;_ > allout-widgets-exposure-undo-processor ()
1141(defun allout-widgets-exposure-undo-processor ()
1142 "Adjust items tracking undo of allout outline structure exposure.
1143
1144Dispatched by `allout-widgets-post-command-business' in response to
1145:undone-exposure entries recorded by `allout-widgets-exposure-undo-recorder'."
1146 (let* ((allout-undo-exposure-in-progress t)
1147 ;; inhibit undo recording while twiddling exposure to track undo:
1148 (widgets allout-widgets-undo-exposure-record)
1149 widget widget-start-marker widget-end-marker
1150 from-state icon-start-point to-state
1151 handled covered)
1152 (setq allout-widgets-undo-exposure-record nil)
1153 (save-excursion
1154 (dolist (widget widgets)
1155 (setq widget-start-marker (widget-get widget :from)
1156 widget-end-marker (widget-get widget :to)
1157 from-state (widget-get widget :icon-state)
1158 icon-start-point (widget-apply widget :actual-position
1159 :icon-start)
1160 to-state (get-text-property icon-start-point
1161 :icon-state))
1162 (setq handled (allout-range-overlaps widget-start-marker
1163 widget-end-marker
1164 handled)
1165 covered (car handled)
1166 handled (cadr handled))
1167 (when (not covered)
1168 (goto-char (widget-get widget :from))
1169 (when (not (allout-hidden-p))
1170 ;; adjust actual exposure to that of to-state viz from-state
1171 (cond ((and (eq to-state 'closed) (eq from-state 'opened))
1172 (allout-hide-current-subtree)
1173 (allout-decorate-item-and-context widget))
1174 ((and (eq to-state 'opened) (eq from-state 'closed))
1175 (save-excursion
1176 (dolist
1177 (expose-to (allout-chart-exposure-contour-by-icon))
1178 (goto-char expose-to)
1179 (allout-show-to-offshoot)))))))))))
1180;;;_ > allout-chart-exposure-contour-by-icon (&optional from-depth)
1181(defun allout-chart-exposure-contour-by-icon (&optional from-depth)
1182 "Return points of subtree items to which exposure should be extended.
1183
1184The qualifying items are ones with a widget icon that is in the closed or
1185empty state, or items with undecorated subitems.
1186
1187The resulting list of points is in reverse order.
1188
1189Optional FROM-DEPTH is for internal use."
1190 ;; During internal recursion, we return a pair: (at-end . result)
1191 ;; Otherwise we just return the result.
1192 (let ((from-depth from-depth)
1193 start-point
1194 at-end level-depth
1195 this-widget
1196 got subgot)
1197 (if from-depth
1198 (setq level-depth (allout-depth))
1199 ;; at containing item:
1200 (setq start-point (point))
1201 (setq from-depth (allout-depth))
1202 (setq at-end (not (allout-next-heading))
1203 level-depth allout-recent-depth))
1204
1205 ;; traverse the level, recursing on deeper levels:
1206 (while (and (not at-end)
1207 (> allout-recent-depth from-depth)
1208 (setq this-widget (allout-get-item-widget)))
1209 (if (< level-depth allout-recent-depth)
1210 ;; recurse:
1211 (progn
1212 (setq subgot (allout-chart-exposure-contour-by-icon level-depth)
1213 at-end (car subgot)
1214 subgot (cdr subgot))
1215 (if subgot (setq got (append subgot got))))
1216 ;; progress at this level:
1217 (when (memq (widget-get this-widget :icon-state) '(closed empty))
1218 (push (point) got)
1219 (allout-end-of-subtree))
1220 (setq at-end (not (allout-next-heading)))))
1221
1222 ;; tailor result depending on whether or not we're a recursion:
1223 (if (not start-point)
1224 (cons at-end got)
1225 (goto-char start-point)
1226 got)))
1227;;;_ > allout-range-overlaps (from to ranges)
1228(defun allout-range-overlaps (from to ranges)
1229 "Return a pair indicating overlap of FROM and TO subtree range in RANGES.
1230
1231First element of result indicates whether candadate range FROM, TO
1232overlapped any of the existing ranges.
1233
1234Second element of result is a new version of RANGES incorporating the
1235candidate range with overlaps consolidated.
1236
1237FROM and TO must be in increasing order, as must be the pairs in RANGES."
1238 ;; to append to the end: (rplacd next-to-last-cdr (list 'f))
1239 (let (new-ranges
1240 entry
1241 ;; the start of the range that includes the candidate from:
1242 included-from
1243 ;; the end of the range that includes the candidate to:
1244 included-to
1245 ;; the candidates were inserted:
1246 done)
1247 (while (and ranges (not done))
1248 (setq entry (car ranges)
1249 ranges (cdr ranges))
1250
1251 (cond
1252
1253 (included-from
1254 ;; some entry included the candidate from.
1255 (cond ((> (car entry) to)
1256 ;; current entry exceeds end of candidate range - done.
1257 (push (list included-from to) new-ranges)
1258 (push entry new-ranges)
1259 (setq included-to to
1260 done t))
1261 ((>= (cadr entry) to)
1262 ;; current entry includes end of candidate range - done.
1263 (push (list included-from (cadr entry)) new-ranges)
1264 (setq included-to (cadr entry)
1265 done t))
1266 ;; current entry contained in candidate range - ditch, continue:
1267 (t nil)))
1268
1269 ((> (car entry) to)
1270 ;; current entry start exceeds candidate end - done, placed as new entry
1271 (push (list from to) new-ranges)
1272 (push entry new-ranges)
1273 (setq included-to to
1274 done t))
1275
1276 ((>= (car entry) from)
1277 ;; current entry start is above candidate start, but not above
1278 ;; candidate end (by prior case).
1279 (setq included-from from)
1280 ;; now we have to check on whether this entry contains to, or continue:
1281 (when (>= (cadr entry) to)
1282 ;; current entry contains only candidate end - done:
1283 (push (list included-from (cadr entry)) new-ranges)
1284 (setq included-to (cadr entry)
1285 done t))
1286 ;; otherwise, we will continue to look for placement of candidate end.
1287 )
1288
1289 ((>= (cadr entry) to)
1290 ;; current entry properly contains candidate range.
1291 (push entry new-ranges)
1292 (setq included-from (car entry)
1293 included-to (cadr entry)
1294 done t))
1295
1296 ((>= (cadr entry) from)
1297 ;; current entry contains start of candidate range.
1298 (setq included-from (car entry)))
1299
1300 (t
1301 ;; current entry is below the candidate range.
1302 (push entry new-ranges))))
1303
1304 (cond ((and included-from included-to)
1305 ;; candidates placed.
1306 nil)
1307 ((not (or included-from included-to))
1308 ;; candidates found no place, must be at the end:
1309 (push (list from to) new-ranges))
1310 (included-from
1311 ;; candidate start placed but end not:
1312 (push (list included-from to) new-ranges))
1313 ;; might be included-to and not included-from, indicating new entry.
1314 )
1315 (setq new-ranges (nreverse new-ranges))
1316 (if ranges (setq new-ranges (append new-ranges ranges)))
1317 (list (if included-from t) new-ranges)))
1318;;;_ > allout-test-range-overlaps ()
1319(defun allout-test-range-overlaps ()
1320 "allout-range-overlaps unit tests."
1321 (let* (ranges
1322 got
1323 (try (lambda (from to)
1324 (setq got (allout-range-overlaps from to ranges))
1325 (setq ranges (cadr got))
1326 got)))
1327;; ;; biggie:
1328;; (setq ranges nil)
1329;; ;; ~ .02 to .1 seconds for just repeated listing args instead of funcall
1330;; ;; ~ 13 seconds for doing repeated funcall
1331;; (message "time-trial: %s, resulting size %s"
1332;; (time-trial
1333;; '(let ((size 10000)
1334;; doing)
1335;; (random t)
1336;; (dotimes (count size)
1337;; (setq doing (random size))
1338;; (funcall try doing (+ doing (random 5)))
1339;; ;;(list doing (+ doing (random 5)))
1340;; )))
1341;; (length ranges))
1342;; (sit-for 2)
1343
1344 ;; fresh:
1345 (setq ranges nil)
1346 (assert (equal (funcall try 3 5) '(nil ((3 5)))))
1347 ;; add range at end:
1348 (assert (equal (funcall try 10 12) '(nil ((3 5) (10 12)))))
1349 ;; add range at beginning:
1350 (assert (equal (funcall try 1 2) '(nil ((1 2) (3 5) (10 12)))))
1351 ;; insert range somewhere in the middle:
1352 (assert (equal (funcall try 7 9) '(nil ((1 2) (3 5) (7 9) (10 12)))))
1353 ;; consolidate some:
1354 (assert (equal (funcall try 5 8) '(t ((1 2) (3 9) (10 12)))))
1355 ;; add more:
1356 (assert (equal (funcall try 15 17) '(nil ((1 2) (3 9) (10 12) (15 17)))))
1357 ;; add more:
1358 (assert (equal (funcall try 20 22)
1359 '(nil ((1 2) (3 9) (10 12) (15 17) (20 22)))))
1360 ;; encompass more:
1361 (assert (equal (funcall try 4 11) '(t ((1 2) (3 12) (15 17) (20 22)))))
1362 ;; encompass all:
1363 (assert (equal (funcall try 2 25) '(t ((1 25)))))
1364
1365 ;; fresh slate:
1366 (setq ranges nil)
1367 (assert (equal (funcall try 20 25) '(nil ((20 25)))))
1368 (assert (equal (funcall try 30 35) '(nil ((20 25) (30 35)))))
1369 (assert (equal (funcall try 26 28) '(nil ((20 25) (26 28) (30 35)))))
1370 (assert (equal (funcall try 15 20) '(t ((15 25) (26 28) (30 35)))))
1371 (assert (equal (funcall try 10 30) '(t ((10 35)))))
1372 (assert (equal (funcall try 5 6) '(nil ((5 6) (10 35)))))
1373 (assert (equal (funcall try 2 100) '(t ((2 100)))))
1374
1375 (setq ranges nil)
1376 ))
1377;;;_ > allout-widgetize-buffer (&optional doing)
1378(defun allout-widgetize-buffer (&optional doing)
1379 "EXAMPLE FUNCTION. Widgetize items in buffer using allout-chart-subtree.
1380
1381We economize by just focusing on the first of local-maximum depth siblings.
1382
1383Optional DOING is for internal use - a chart of the current level, for
1384recursive operation."
1385
1386 (interactive)
1387 (if (not doing)
1388
1389 (save-excursion
1390 (goto-char (point-min))
1391 ;; Construct the chart by scanning the siblings:
1392 (dolist (top-level-sibling (allout-chart-siblings))
1393 (goto-char top-level-sibling)
1394 (let ((subchart (allout-chart-subtree)))
1395 (if subchart
1396 (allout-widgetize-buffer subchart)))))
1397
1398 ;; save-excursion was done on recursion entry, not necessary here.
1399 (let (have-sublists)
1400 (dolist (sibling doing)
1401 (when (listp sibling)
1402 (setq have-sublists t)
1403 (allout-widgetize-buffer sibling)))
1404 (when (and (not have-sublists) (not (widget-at (car doing))))
1405 (goto-char (car doing))
1406 (allout-get-or-create-item-widget)))))
1407
1408;;;_ : Item widget and constructors
1409
1410;;;_ $ allout-item-widget
1411(define-widget 'allout-item-widget 'default
1412 "A widget presenting an allout outline item."
1413
1414 'button nil
1415 ;; widget-field-at respects this to get item if 'field is unused.
1416 ;; we don't use field to avoid collision with end-of-line, etc, on which
1417 ;; allout depends.
1418 'real-field nil
1419
1420 ;; data fields:
1421
1422
1423 ;; tailor the widget for a specific item
1424 :create 'allout-decorate-item-and-context
1425 :value-delete 'allout-widgets-undecorate-item
1426 ;; Not Yet Converted (from original, tree-widget stab)
1427 :expander 'allout-tree-event-dispatcher ; get children when nil :args
1428 :expander-p 'identity ; always engage the :expander
1429 :action 'allout-tree-widget-action
1430 ;; :notify "when item changes"
1431
1432 ;; force decoration of item but not context, unless already done this tick:
1433 :redecorate 'allout-redecorate-item
1434 :last-decorated-tick nil
1435 ;; recognize the actual situation of the item's text:
1436 :parse-item 'allout-parse-item-at-point
1437 ;; decorate the entirety of the item, sans offspring:
1438 :decorate-item-span 'allout-decorate-item-span
1439 ;; decorate the various item elements:
1440 :decorate-guides 'allout-decorate-item-guides
1441 :decorate-icon 'allout-decorate-item-icon
1442 :decorate-cue 'allout-decorate-item-cue
1443 :decorate-body 'allout-decorate-item-body
1444 :actual-position 'allout-item-actual-position
1445
1446 ;; Layout parameters:
1447 :is-container nil ; is this actually the encompassing file/connection?
1448
1449 :from nil ; item beginning - marker
1450 :to nil ; item end - marker
1451 :span-overlay nil ; overlay by which actual postion is determined
1452
1453 ;; also serves as guide-end:
1454 :icon-start nil
1455 :icon-end nil
1456 :distinctive-start nil
1457 ;; also serves as cue-start:
1458 :distinctive-end nil
1459 ;; also serves as cue-end:
1460 :body-start nil
1461 :body-end nil
1462 :depth nil
1463 :has-subitems nil
1464 :was-has-subitems 'init
1465 :expanded nil
1466 :was-expanded 'init
1467 :brief nil
1468 :was-brief 'init
1469
1470 :does-encrypt nil ; pending encryption when :is-encrypted false.
1471 :is-encrypted nil
1472
1473 ;; the actual location of the item text:
1474 :location 'allout-item-location
1475
1476 :button-keymap allout-item-icon-keymap ; XEmacs
1477 :keymap allout-item-icon-keymap ; Emacs
1478
1479 ;; Element regions:
1480 :guides-span nil
1481 :icon-span nil
1482 :cue-span nil
1483 :bullet nil
1484 :was-bullet nil
1485 :body-span nil
1486
1487 :body-brevity-p 'allout-body-brevity-p
1488
1489 ;; :guide-column-flags indicate (in reverse order) whether or not the
1490 ;; item's ancestor at the depth corresponding to the column has a
1491 ;; subsequent sibling - ie, whether or not the corresponding column needs
1492 ;; a descender line to connect that ancestor with its sibling.
1493 :guide-column-flags nil
1494 :was-guide-column-flags 'init
1495
1496 ;; ie, has subitems:
1497 :populous-p 'allout-item-populous-p
1498 :help-echo 'allout-tree-widget-help-echo
1499 )
1500;;;_ > allout-new-item-widget ()
1501(defsubst allout-new-item-widget ()
1502 "create a new item widget, not yet situated anywhere."
1503 (if allout-widgets-maintain-tally
1504 ;; all the extra overhead is incurred only when doing the
1505 ;; maintenance, except the condition, which can't be avoided.
1506 (let ((widget (widget-convert 'allout-item-widget)))
1507 (puthash widget nil allout-widgets-tally)
1508 widget)
1509 (widget-convert 'allout-item-widget)))
1510;;;_ : Item decoration
1511;;;_ > allout-decorate-item-and-context (item-widget &optional redecorate
1512;;; blank-container parent)
1513(defun allout-decorate-item-and-context (item-widget &optional redecorate
1514 blank-container parent)
1515 "Create or adjust widget decorations for ITEM-WIDGET and neighbors at point.
1516
1517The neighbors include its siblings and parent.
1518
1519ITEM-WIDGET can be a created or converted allout-item-widget.
1520
1521If you're only trying to get or create a widget for an item, use
1522`allout-get-or-create-item-widget'. If you have the item-widget, applying
1523:redecorate will do the right thing.
1524
1525Optional BLANK-CONTAINER is for internal use. It is used to fabricate a
1526container widget for an empty-bodied container, in the course of decorating
1527a proper \(non-container\) item which starts at the beginning of the file.
1528
1529Optional REDECORATE causes redecoration of the item-widget and
1530its siblings, even if already decorated in this cycle of the command loop.
1531
1532Optional PARENT, when provided, bypasses some navigation and computation
1533necessary to obtain the parent of the items being processed.
1534
1535We return the item-widget corresponding to the item at point."
1536
1537 (when (or redecorate
1538 (not (equal (widget-get item-widget :last-decorated-tick)
1539 allout-command-counter)))
1540 (let* ((allout-inhibit-body-modification-hook t)
1541 (was-modified (buffer-modified-p))
1542 (was-point (point))
1543 prefix-start
1544 (is-container (or blank-container
1545 (not (setq prefix-start (allout-goto-prefix)))
1546 (< was-point prefix-start)))
1547 ;; steady-point (set in two steps) is reliable across parent
1548 ;; widget-creation.
1549 (steady-point (progn (if is-container (goto-char 1))
1550 (point-marker)))
1551 (steady-point (progn (set-marker-insertion-type steady-point t)
1552 steady-point))
1553 (parent (and (not is-container)
1554 (allout-get-or-create-parent-widget)))
1555 parent-flags parent-depth
1556 successor-sibling
1557 body
1558 doing-item
1559 sub-item-widget
1560 depth
1561 reverse-siblings-chart
1562 (buffer-undo-list t))
1563
1564 ;; At this point the parent is decorated and parent-flags indicate
1565 ;; its guide lines. We will iterate over the siblings according to a
1566 ;; chart we create at the start, and going from last to first so we
1567 ;; don't have to worry about text displacement caused by widgetizing.
1568
1569 (if is-container
1570 (progn (widget-put item-widget :is-container t)
1571 (setq reverse-siblings-chart (list 1)))
1572 (goto-char (widget-apply parent :actual-position :from))
1573 (if (widget-get parent :is-container)
1574 ;; `allout-goto-prefix' will go to first non-container item:
1575 (allout-goto-prefix)
1576 (allout-next-heading))
1577 (setq depth (allout-recent-depth))
1578 (setq reverse-siblings-chart (list allout-recent-prefix-beginning))
1579 (while (allout-next-sibling)
1580 (push allout-recent-prefix-beginning reverse-siblings-chart)))
1581
1582 (dolist (doing-at reverse-siblings-chart)
1583 (goto-char doing-at)
1584 (when allout-widgets-track-decoration
1585 (sit-for 0))
1586
1587 (setq doing-item (if (= doing-at steady-point)
1588 item-widget
1589 (or (allout-get-item-widget)
1590 (allout-new-item-widget))))
1591
1592 (when (or redecorate (not (equal (widget-get doing-item
1593 :last-decorated-tick)
1594 allout-command-counter)))
1595 (widget-apply doing-item :parse-item t blank-container)
1596 (widget-apply doing-item :decorate-item-span)
1597
1598 (widget-apply doing-item :decorate-guides
1599 parent (and successor-sibling t))
1600 (widget-apply doing-item :decorate-icon)
1601 (widget-apply doing-item :decorate-cue)
1602 (widget-apply doing-item :decorate-body)
1603
1604 (widget-put doing-item :last-decorated-tick allout-command-counter))
1605
1606 (setq successor-sibling doing-at))
1607
1608 (set-buffer-modified-p was-modified)
1609 (goto-char steady-point)
1610 ;; must null the marker or the buffer gets clogged with impedence:
1611 (set-marker steady-point nil)
1612
1613 item-widget)))
1614;;;_ > allout-redecorate-item (item)
1615(defun allout-redecorate-item (item-widget)
1616 "Resituate ITEM-WIDGET decorations, disregarding context.
1617
1618Use this to redecorate only the item, when you know that it's
1619situation with respect to siblings, parent, and offspring is
1620unchanged from its last decoration. Use
1621`allout-decorate-item-and-context' instead to reassess and adjust
1622relevent context, when suitable."
1623 (if (not (equal (widget-get item-widget :last-decorated-tick)
1624 allout-command-counter))
1625 (let ((was-modified (buffer-modified-p))
1626 (buffer-undo-list t))
1627 (widget-apply item-widget :parse-item)
1628 (widget-apply item-widget :decorate-guides)
1629 (widget-apply item-widget :decorate-icon)
1630 (widget-apply item-widget :decorate-cue)
1631 (widget-apply item-widget :decorate-body)
1632 (set-buffer-modified-p was-modified))))
1633;;;_ > allout-redecorate-visible-subtree (&optional parent-widget
1634;;; depth chart)
1635(defun allout-redecorate-visible-subtree (&optional parent-widget depth chart)
1636 "Redecorate all visible items in subtree at point.
1637
1638Optional PARENT-WIDGET is for optimization, when the parent
1639widget is already available.
1640
1641Optional DEPTH restricts the excursion depth of covered.
1642
1643Optional CHART is for internal recursion, to carry a chart of the
1644target items.
1645
1646Point is left at the last sibling in the visible subtree."
1647 ;; using a treatment that takes care of all the siblings on a level, we
1648 ;; only need apply it to the first sibling on the level, and we can
1649 ;; collect and pass the parent of the lower levels to recursive calls as
1650 ;; we go.
1651 (let ((parent-widget
1652 (if (and parent-widget (widget-apply parent-widget
1653 :actual-position :from))
1654 (progn (goto-char (widget-apply parent-widget
1655 :actual-position :from))
1656 parent-widget)
1657 (let ((got (allout-get-item-widget)))
1658 (if got
1659 (allout-decorate-item-and-context got 'redecorate)
1660 (allout-get-or-create-item-widget 'redecorate)))))
1661 (pending-chart (or chart (allout-chart-subtree nil 'visible)))
1662 item-widget
1663 previous-sibling-point
1664 previous-sibling
1665 recent-sibling-point)
1666 (setq pending-chart (nreverse pending-chart))
1667 (dolist (sibling-point pending-chart)
1668 (cond ((integerp sibling-point)
1669 (when (not previous-sibling-point)
1670 (goto-char sibling-point)
1671 (if (setq item-widget (allout-get-item-widget nil))
1672 (allout-decorate-item-and-context item-widget 'redecorate
1673 nil parent-widget)
1674 (allout-get-or-create-item-widget)))
1675 (setq previous-sibling-point sibling-point
1676 recent-sibling-point sibling-point))
1677 ((listp sibling-point)
1678 (if (or (not depth)
1679 (> depth 1))
1680 (allout-redecorate-visible-subtree
1681 (if (not previous-sibling-point)
1682 ;; containment discontinuity - sigh
1683 parent-widget
1684 (allout-get-or-create-item-widget 'redecorate))
1685 (if depth (1- depth))
1686 sibling-point)))))
1687 (if (and recent-sibling-point (< (point) recent-sibling-point))
1688 (goto-char recent-sibling-point))))
1689;;;_ > allout-parse-item-at-point (item-widget &optional at-beginning
1690;;; blank-container)
1691(defun allout-parse-item-at-point (item-widget &optional at-beginning
1692 blank-container)
1693 "Set widget ITEM-WIDGET layout parameters per item-at-point's actual layout.
1694
1695If optional AT-BEGINNING is t, then point is assumed to be at the start of
1696the item prefix.
1697
1698If optional BLANK-CONTAINER is true, then the parameters of a container
1699which has an empty body are set. \(Though the body is blank, the object
1700may have subitems.\)"
1701
1702 ;; Uncomment this sit-for to notice where decoration is happening:
1703;; (sit-for .1)
1704 (let* ((depth (allout-depth))
1705 (depth (if blank-container 0 depth))
1706 (is-container (or blank-container (zerop depth)))
1707
1708 (does-encrypt (and (not is-container)
1709 (allout-encrypted-type-prefix)))
1710 (is-encrypted (and does-encrypt (allout-encrypted-topic-p)))
1711 (icon-end allout-recent-prefix-end)
1712 (icon-start (1- icon-end))
1713 body-start
1714 body-end
1715 bullet
1716 has-subitems
1717 (contents-depth (1+ depth))
1718 )
1719 (widget-put item-widget :depth depth)
1720 (if is-container
1721
1722 (progn
1723 (widget-put item-widget :from (allout-set-boundary-marker
1724 :from (point-min)
1725 (widget-get item-widget :from)))
1726 (widget-put item-widget :icon-end nil)
1727 (widget-put item-widget :icon-start nil)
1728 (setq body-start (widget-put item-widget :body-start 1)))
1729
1730 ;; not container:
1731
1732 (widget-put item-widget :from (allout-set-boundary-marker
1733 :from (if at-beginning
1734 (point)
1735 allout-recent-prefix-beginning)
1736 (widget-get item-widget :from)))
1737 (widget-put item-widget :icon-start icon-start)
1738 (widget-put item-widget :icon-end icon-end)
1739 (when does-encrypt
1740 (widget-put item-widget :does-encrypt t)
1741 (widget-put item-widget :is-encrypted is-encrypted))
1742
1743 ;; cue area:
1744 (setq body-start icon-end)
1745 (widget-put item-widget :bullet (setq bullet (allout-get-bullet)))
1746 (if (equal (char-after body-start) ? )
1747 (setq body-start (1+ body-start)))
1748 (widget-put item-widget :body-start body-start)
1749 )
1750
1751 ;; Both container and regular items:
1752
1753 ;; :body-end (doesn't include a trailing blank line, if any) -
1754 (widget-put item-widget :body-end (setq body-end
1755 (if blank-container
1756 1
1757 (allout-end-of-entry))))
1758
1759 (widget-put item-widget :to (allout-set-boundary-marker
1760 :to (if blank-container
1761 (point-min)
1762 (or (allout-pre-next-prefix)
1763 (goto-char (point-max))))
1764 (widget-get item-widget :to)))
1765 (widget-put item-widget :has-subitems
1766 (setq has-subitems
1767 (and
1768 ;; has a subsequent item:
1769 (not (= body-end (point-max)))
1770 ;; subsequent item is deeper:
1771 (< depth (setq contents-depth (allout-recent-depth))))))
1772 ;; note :expanded - true if widget item's content is currently visible?
1773 (widget-put item-widget :expanded
1774 (and has-subitems
1775 ;; subsequent item is or isn't visible:
1776 (save-excursion
1777 (goto-char allout-recent-prefix-beginning)
1778 (not (allout-hidden-p)))))))
1779;;;_ > allout-set-boundary-marker (boundary position &optional current-marker)
1780(defun allout-set-boundary-marker (boundary position &optional current-marker)
1781 "Set or create item widget BOUNDARY type marker at POSITION.
1782
1783Optional CURRENT-MARKER is the marker currently being used for
1784the boundary, if any.
1785
1786BOUNDARY type is either :from or :to, determining the marker insertion type."
1787 (if (not position) (setq position (point)))
1788 (if current-marker
1789 (set-marker current-marker position)
1790 (let ((marker (make-marker)))
1791 ;; XXX dang - would like for :from boundary to advance after inserted
1792 ;; text, but that would omit new header prefixes when allout
1793 ;; relevels, etc. this competes with ad-hoc edits, which would
1794 ;; better be omitted
1795 (set-marker-insertion-type marker nil)
1796 (set-marker marker position))))
1797;;;_ > allout-decorate-item-span (item-widget)
1798(defun allout-decorate-item-span (item-widget)
1799 "Equip the item with a span, as an entirety.
1800
1801This span is implemented so it can be used to detect displacement
1802of the widget in absolute terms, and provides an offset bias for
1803the various element spans."
1804
1805 (if (and (widget-get item-widget :is-container)
1806 ;; the only case where the span could be empty.
1807 (eq (widget-get item-widget :from)
1808 (widget-get item-widget :to)))
1809 nil
1810 (allout-item-span item-widget
1811 (widget-get item-widget :from)
1812 (widget-get item-widget :to))))
1813;;;_ > allout-decorate-item-guides (item-widget
1814;;; &optional parent-widget has-successor)
1815(defun allout-decorate-item-guides (item-widget
1816 &optional parent-widget has-successor)
1817 "Add ITEM-WIDGET guide icon-prefix descender and connector text properties.
1818
1819Optional arguments provide context for deriving the guides. In
1820their absence, the current guide column flags are used.
1821
1822Optional PARENT-WIDGET is the widget for the item's parent item.
1823
1824Optional HAS-SUCCESSOR is true iff the item is followed by a sibling.
1825
1826We also hide the header-prefix string.
1827
1828Guides are established according to the item-widget's :guide-column-flags,
1829when different than :was-guide-column-flags. Changing that property and
1830reapplying this method will rectify the glyphs."
1831
1832 (when (not (widget-get item-widget :is-container))
1833 (let* ((depth (widget-get item-widget :depth))
1834 (parent-depth (and parent-widget
1835 (widget-get parent-widget :depth)))
1836 (parent-flags (and parent-widget
1837 (widget-get parent-widget :guide-column-flags)))
1838 (parent-flags-depth (length parent-flags))
1839 (extender-length (- depth (+ parent-flags-depth 2)))
1840 (flags (or (and (> depth 1)
1841 parent-widget
1842 (widget-put item-widget :guide-column-flags
1843 (append (list has-successor)
1844 (if (< 0 extender-length)
1845 (make-list extender-length
1846 '-))
1847 parent-flags)))
1848 (widget-get item-widget :guide-column-flags)))
1849 (was-flags (widget-get item-widget :was-guide-column-flags))
1850 (guides-start (widget-get item-widget :from))
1851 (guides-end (widget-get item-widget :icon-start))
1852 (position guides-start)
1853 (increment (length allout-header-prefix))
1854 reverse-flags
1855 guide-name
1856 extenders paint-extenders
1857 (inhibit-read-only t))
1858
1859 (when (not (equal was-flags flags))
1860
1861 (setq reverse-flags (reverse flags))
1862 (while reverse-flags
1863 (setq guide-name
1864 (cond ((null (cdr reverse-flags))
1865 (if (car reverse-flags)
1866 'mid-connector
1867 'end-connector))
1868 ((eq (car reverse-flags) '-)
1869 ;; accumulate extenders tally, to be painted on next
1870 ;; non-extender flag, according to the flag type.
1871 (setq extenders (1+ (or extenders 0)))
1872 nil)
1873 ((car reverse-flags)
1874 'through-descender)
1875 (t 'skip-descender)))
1876 (when guide-name
1877 (put-text-property position (setq position (+ position increment))
1878 'display (allout-fetch-icon-image guide-name))
1879 (if (> increment 1) (setq increment 1))
1880 (when extenders
1881 ;; paint extenders after a connector, else leave spaces.
1882 (dotimes (i extenders)
1883 (put-text-property
1884 position (setq position (1+ position))
1885 'display (allout-fetch-icon-image
1886 (if (memq guide-name '(mid-connector end-connector))
1887 'extender-connector
1888 'skip-descender))))
1889 (setq extenders nil)))
1890 (setq reverse-flags (cdr reverse-flags)))
1891 (widget-put item-widget :was-guide-column-flags flags))
1892
1893 (allout-item-element-span-is item-widget :guides-span
1894 guides-start guides-end))))
1895;;;_ > allout-decorate-item-icon (item-widget)
1896(defun allout-decorate-item-icon (item-widget)
1897 "Add item icon glyph and distinctive bullet text properties to ITEM-WIDGET."
1898
1899 (when (not (widget-get item-widget :is-container))
1900 (let* ((icon-start (widget-get item-widget :icon-start))
1901 (icon-end (widget-get item-widget :icon-end))
1902 (bullet (widget-get item-widget :bullet))
1903 (use-bullet bullet)
1904 (was-bullet (widget-get item-widget :was-bullet))
1905 (distinctive (allout-distinctive-bullet bullet))
1906 (distinctive-start (widget-get item-widget :distinctive-start))
1907 (distinctive-end (widget-get item-widget :distinctive-end))
1908 (does-encrypt (widget-get item-widget :does-encrypt))
1909 (is-encrypted (and does-encrypt (widget-get item-widget
1910 :is-encrypted)))
1911 (expanded (widget-get item-widget :expanded))
1912 (has-subitems (widget-get item-widget :has-subitems))
1913 (inhibit-read-only t)
1914 icon-state)
1915
1916 (when (not (and (equal (widget-get item-widget :was-expanded) expanded)
1917 (equal (widget-get item-widget :was-has-subitems)
1918 has-subitems)
1919 (equal (widget-get item-widget :was-does-encrypt)
1920 does-encrypt)
1921 (equal (widget-get item-widget :was-is-encrypted)
1922 is-encrypted)))
1923
1924 (setq icon-state
1925 (cond (does-encrypt (if is-encrypted
1926 'locked-encrypted
1927 'unlocked-encrypted))
1928 (expanded 'opened)
1929 (has-subitems 'closed)
1930 (t 'empty)))
1931 (put-text-property icon-start (1+ icon-start)
1932 'display (allout-fetch-icon-image icon-state))
1933 (widget-put item-widget :was-expanded expanded)
1934 (widget-put item-widget :was-has-subitems has-subitems)
1935 (widget-put item-widget :was-does-encrypt does-encrypt)
1936 (widget-put item-widget :was-is-encrypted is-encrypted)
1937 ;; preserve as a widget property to track last known:
1938 (widget-put item-widget :icon-state icon-state)
1939 ;; preserve as a text property to track undo:
1940 (put-text-property icon-start icon-end :icon-state icon-state))
1941 (allout-item-element-span-is item-widget :icon-span
1942 icon-start icon-end)
1943 (when (not (string= was-bullet bullet))
1944 (cond ((not distinctive)
1945 ;; XXX we strip the prior properties without even checking if
1946 ;; the prior bullet was distinctive, because the widget
1947 ;; provisions to convey that info is disappearing, sigh.
1948 (remove-text-properties icon-end (1+ icon-end) '(display))
1949 (setq distinctive-start icon-end distinctive-end icon-end)
1950 (widget-put item-widget :distinctive-start distinctive-start)
1951 (widget-put item-widget :distinctive-end distinctive-end))
1952
1953 ((not (string= bullet allout-numbered-bullet))
1954 (setq distinctive-start icon-end distinctive-end (+ icon-end 1)))
1955
1956 (does-encrypt
1957 (setq distinctive-start icon-end distinctive-end (+ icon-end 1)))
1958
1959 (t
1960 (goto-char icon-end)
1961 (looking-at "[0-9]+")
1962 (setq use-bullet (buffer-substring icon-end (match-end 0)))
1963 (setq distinctive-start icon-end
1964 distinctive-end (match-end 0))))
1965 (put-text-property distinctive-start distinctive-end 'display
1966 use-bullet)
1967 (widget-put item-widget :was-bullet bullet)
1968 (widget-put item-widget :distinctive-start distinctive-start)
1969 (widget-put item-widget :distinctive-end distinctive-end)))))
1970;;;_ > allout-decorate-item-cue (item-widget)
1971(defun allout-decorate-item-cue (item-widget)
1972 "Incorporate space between bullet icon and body to the ITEM-WIDGET."
1973 ;; NOTE: most of the cue-area
1974
1975 (when (not (widget-get item-widget :is-container))
1976 (let* ((cue-start (or (widget-get item-widget :distinctive-end)
1977 (widget-get item-widget :icon-end)))
1978 (body-start (widget-get item-widget :body-start))
1979 (expanded (widget-get item-widget :expanded))
1980 (has-subitems (widget-get item-widget :has-subitems))
1981 (inhibit-read-only t))
1982
1983 (allout-item-element-span-is item-widget :cue-span cue-start body-start)
1984 (put-text-property (1- body-start) body-start 'rear-nonsticky t))))
1985;;;_ > allout-decorate-item-body (item-widget &optional force)
1986(defun allout-decorate-item-body (item-widget &optional force)
1987 "Incorporate item body text as part the ITEM-WIDGET.
1988
1989Optional FORCE means force reassignment of the region property."
1990
1991 (let* ((allout-inhibit-body-modification-hook t)
1992 (body-start (widget-get item-widget :body-start))
1993 (body-end (widget-get item-widget :body-end))
1994 (body-text-end body-end)
1995 (inhibit-read-only t))
1996
1997 (allout-item-element-span-is item-widget :body-span
1998 body-start (min (1+ body-end) (point-max))
1999 force)))
2000;;;_ > allout-item-actual-position (item-widget field)
2001(defun allout-item-actual-position (item-widget field)
2002 "Return ITEM-WIDGET FIELD position taking item displacement into account."
2003
2004 ;; The item's sub-element positions (:icon-end, :body-start, etc) are
2005 ;; accurate when the item is parsed, but some offsets from the start
2006 ;; drift with text added in the body.
2007 ;;
2008 ;; Rather than reparse an item with every change (inefficient), or derive
2009 ;; every position from a distinct field marker/overlay (prohibitive as
2010 ;; the number of items grows), we use the displacement tracking of the
2011 ;; :span-overlay's markers, against the registered :from or :body-end
2012 ;; (depending on whether the requested field value is before or after the
2013 ;; item body), to bias the registered values.
2014 ;;
2015 ;; This is not necessary/useful when the item is being decorated, because
2016 ;; that always must be preceeded by a fresh item parse.
2017
2018 (if (not (eq field :body-end))
2019 (widget-get item-widget :from)
2020
2021 (let* ((span-overlay (widget-get item-widget :span-overlay))
2022 (body-end-position (widget-get item-widget :body-end))
2023 (ref-marker-position (and span-overlay
2024 (overlay-end span-overlay)))
2025 (offset (and body-end-position span-overlay
2026 (- (or ref-marker-position 0)
2027 body-end-position))))
2028 (+ (widget-get item-widget field) (or offset 0)))))
2029;;;_ : Item undecoration
2030;;;_ > allout-widgets-undecorate-region (start end)
2031(defun allout-widgets-undecorate-region (start end)
2032 "Eliminate widgets and decorations for all items in region from START to END."
2033 (let ((next start)
2034 widget)
2035 (save-excursion
2036 (goto-char start)
2037 (while (< (setq next (next-single-char-property-change next
2038 'display
2039 (current-buffer)
2040 end))
2041 end)
2042 (goto-char next)
2043 (when (setq widget (allout-get-item-widget))
2044 ;; if the next-property/overly progression got us to a widget:
2045 (allout-widgets-undecorate-item widget t))))))
2046;;;_ > allout-widgets-undecorate-text (text)
2047(defun allout-widgets-undecorate-text (text)
2048 "Eliminate widgets and decorations for all items in TEXT."
2049 (remove-text-properties 0 (length text)
2050 '(display nil :icon-state nil rear-nonsticky nil
2051 category nil button nil field nil)
2052 text)
2053 text)
2054;;;_ > allout-widgets-undecorate-item (item-widget &optional no-expose)
2055(defun allout-widgets-undecorate-item (item-widget &optional no-expose)
2056 "Remove widget decorations from ITEM-WIDGET.
2057
2058Any concealed content head lines and item body is exposed, unless
2059optional NO-EXPOSE is non-nil."
2060 (let ((from (widget-get item-widget :from))
2061 (to (widget-get item-widget :to))
2062 (text-properties-to-remove '(display nil
2063 :icon-state nil
2064 rear-nonsticky nil
2065 category nil
2066 button nil
2067 field nil))
2068 (span-overlay (widget-get item-widget :span-overlay))
2069 (button-overlay (widget-get item-widget :button))
2070 (was-modified (buffer-modified-p))
2071 (buffer-undo-list t)
2072 (inhibit-read-only t))
2073 (if (not no-expose)
2074 (allout-flag-region from to nil))
2075 (allout-unprotected
2076 (remove-text-properties from to text-properties-to-remove))
2077 (when span-overlay
2078 (delete-overlay span-overlay) (widget-put item-widget :span-overlay nil))
2079 (when button-overlay
2080 (delete-overlay button-overlay) (widget-put item-widget :button nil))
2081 (set-marker from nil)
2082 (set-marker to nil)
2083 (if (not was-modified)
2084 (set-buffer-modified-p nil))))
2085
2086;;;_ : Item decoration support
2087;;;_ > allout-item-span (item-widget &optional start end)
2088(defun allout-item-span (item-widget &optional start end)
2089 "Return or register the location of an ITEM-WIDGET's actual START and END.
2090
2091If START and END are not passed in, return either a dotted pair
2092of the current span, if established, or nil if not yet set.
2093
2094When the START and END are passed, return the distance that the
2095start of the item moved. We return 0 if the span was not
2096previously established or is not moved."
2097 (let ((overlay (widget-get item-widget :span-overlay))
2098 was-start was-end
2099 changed)
2100 (cond ((not overlay) (when start
2101 (setq overlay (make-overlay start end nil t nil))
2102 (overlay-put overlay 'button item-widget)
2103 (widget-put item-widget :span-overlay overlay)
2104 t))
2105 ;; report:
2106 ((not start) (cons (overlay-start overlay) (overlay-end overlay)))
2107 ;; move:
2108 ((or (not (equal (overlay-start overlay) start))
2109 (not (equal (overlay-end overlay) end)))
2110 (move-overlay overlay start end)
2111 t)
2112 ;; specified span already set:
2113 (t nil))))
2114;;;_ > allout-item-element-span-is (item-widget element
2115;;; &optional start end force)
2116(defun allout-item-element-span-is (item-widget element
2117 &optional start end force)
2118 "Return or register the location of the indicated ITEM-WIDGET ELEMENT.
2119
2120ELEMENT is one of :guides-span, :icon-span, :cue-span, or :body-span.
2121
2122When optional START is specified, optional END must also be.
2123
2124START and END are the actual bounds of the region, if provided.
2125
2126If START and END are not passed in, we return either a dotted
2127pair of the current span, if established, or nil if not yet set.
2128
2129When the START and END are passed, we return t if the region
2130changed or nil if not.
2131
2132Optional FORCE means force assignment of the region's text
2133property, even if it's already set."
2134 (let ((span (widget-get item-widget element)))
2135 (cond ((or (not span) force)
2136 (when start
2137 (widget-put item-widget element (cons start end))
2138 (put-text-property start end 'category
2139 (cdr (assoc element
2140 allout-span-to-category)))
2141 t))
2142 ;; report:
2143 ((not start) span)
2144 ;; move if necessary:
2145 ((not (and (eq (car span) start)
2146 (eq (cdr span) end)))
2147 (widget-put item-widget element span)
2148 t)
2149 ;; specified span already set:
2150 (t nil))))
2151;;;_ : Item widget retrieval (/ high-level creation):
2152;;;_ > allout-get-item-widget (&optional container)
2153(defun allout-get-item-widget (&optional container)
2154 "Return the widget for the item at point, or nil if no widget yet exists.
2155
2156Point must be situated *before* the start of the target item's
2157body, so we don't get an existing containing item when we're in
2158the process of creating an item in the middle of another.
2159
2160Optional CONTAINER is used to obtain the container item."
2161 (if (or container (zerop (allout-depth)))
2162 allout-container-item-widget
2163 ;; allout-recent-* are calibrated by (allout-depth) if we got here.
2164 (let ((got (widget-at allout-recent-prefix-beginning)))
2165 (if (and got (listp got))
2166 (if (marker-position (widget-get got :from))
2167 (and
2168 (>= (point) (widget-apply got :actual-position :from))
2169 (<= (point) (widget-apply got :actual-position :body-start))
2170 got)
2171 ;; a wacky residual item - undecorate and disregard:
2172 (allout-widgets-undecorate-item got)
2173 nil)))))
2174;;;_ > allout-get-or-create-item-widget (&optional redecorate blank-container)
2175(defun allout-get-or-create-item-widget (&optional redecorate blank-container)
2176 "Return a widget for the item at point, creating the widget if necessary.
2177
2178When creating a widget, we assume there has been a context change
2179and decorate its siblings and parent, as well.
2180
2181Optional BLANK-CONTAINER is for internal use, to fabricate a
2182meta-container item with an empty body when the first proper
2183\(non-container\) item starts at the beginning of the file.
2184
2185Optional REDECORATE, if non-nil, means to redecorate the widget
2186if it already exists."
2187 (let ((widget (allout-get-item-widget blank-container))
2188 (buffer-undo-list t))
2189 (cond (widget (if redecorate
2190 (allout-redecorate-item widget))
2191 widget)
2192 ((or blank-container (zerop (allout-depth)))
2193 (or allout-container-item-widget
2194 (setq allout-container-item-widget
2195 (allout-decorate-item-and-context
2196 (widget-convert 'allout-item-widget)
2197 nil blank-container))))
2198 ;; create a widget for a regular/non-container item:
2199 (t (allout-decorate-item-and-context (widget-convert
2200 'allout-item-widget))))))
2201;;;_ > allout-get-or-create-parent-widget (&optional redecorate)
2202(defun allout-get-or-create-parent-widget (&optional redecorate)
2203 "Return widget for parent of item at point, decorating it if necessary.
2204
2205We return the container widget if we're above the first proper item in the
2206file.
2207
2208Optional REDECORATE, if non-nil, means to redecorate the widget if it
2209already exists.
2210
2211Point will wind up positioned on the beginning of the parent or beginning
2212of the buffer."
2213 ;; use existing widget, if there, else establish it
2214 (if (or (bobp) (and (not (allout-ascend))
2215 (looking-at allout-regexp)))
2216 (allout-get-or-create-item-widget redecorate 'blank-container)
2217 (allout-get-or-create-item-widget redecorate)))
2218;;;_ : X- Item ancillaries
2219;;;_ >X allout-body-modification-handler (beg end)
2220(defun allout-body-modification-handler (beg end)
2221 "Do routine processing of body text before and after modification.
2222
2223Operation is inhibited by `allout-inhibit-body-modification-handler'."
2224
2225;; The primary duties are:
2226;;
2227;; - marking of escaped prefix-like text for delayed cleanup of escapes
2228;; - removal and replacement of the settings
2229;; - maintenance of beginning-of-line guide lines
2230;;
2231;; ?? Escapes removal \(before changes\) is not done when edits span multiple
2232;; items, recognizing that item structure is being preserved, including
2233;; escaping of item-prefix-like text within bodies. See
2234;; `allout-before-modification-handler' and
2235;; `allout-inhibit-body-modification-handler'.
2236;;
2237;; Adds the overlay to the `allout-unresolved-body-mod-workhash' during
2238;; before-change operation, and removes from that list during after-change
2239;; operation.
2240 (cond (allout-inhibit-body-modification-hook nil)))
2241;;;_ >X allout-graphics-modification-handler (beg end)
2242(defun allout-graphics-modification-handler (beg end)
2243 "Protect against incoherent deletion of decoration graphics.
2244
2245Deletes allowed only when inhibit-read-only is t."
2246 (cond
2247 (undo-in-progress (when (eq (get-text-property beg 'category)
2248 'allout-icon-span-category)
2249 (save-excursion
2250 (goto-char beg)
2251 (let* ((item-widget (allout-get-item-widget)))
2252 (if item-widget
2253 (allout-widgets-exposure-undo-recorder
2254 item-widget))))))
2255 (inhibit-read-only t)
2256 ((not (and (boundp 'allout-mode) allout-mode)) t)
2257 ((equal this-command 'quoted-insert) t)
2258 ((yes-or-no-p "Unruly edit of outline structure - allow? ")
2259 (setq allout-widgets-unset-inhibit-read-only (not inhibit-read-only)
2260 inhibit-read-only t))
2261 (t (error
2262 (substitute-command-keys allout-structure-unruly-deletion-message)))))
2263;;;_ > allout-item-icon-key-handler ()
2264(defun allout-item-icon-key-handler ()
2265 "Catchall handling of key bindings in item icon/cue hot-spots.
2266
2267Applies `allout-hotspot-key-handler' and calls the result, if any, as an
2268interactive command."
2269
2270 (interactive)
2271 (let* ((mapped-binding (allout-hotspot-key-handler)))
2272 (when mapped-binding
2273 (call-interactively mapped-binding))))
2274
2275;;;_ : Status
2276;;;_ . allout-item-location (item-widget)
2277(defun allout-item-location (item-widget)
2278 "Location of the start of the item's text."
2279 (overlay-start (widget-get item-widget :span-overlay)))
2280
2281;;;_ : Icon management
2282;;;_ > allout-fetch-icon-image (name)
2283(defun allout-fetch-icon-image (name)
2284 "Fetch allout icon for symbol NAME.
2285
2286We use a caching strategy, so the caller doesn't need to do so."
2287 (let* ((types allout-widgets-icon-types)
2288 (use-dir (if (equal (allout-frame-property nil 'background-mode)
2289 'light)
2290 allout-widgets-icons-light-subdir
2291 allout-widgets-icons-dark-subdir))
2292 (key (list name use-dir))
2293 (got (assoc key allout-widgets-icons-cache)))
2294 (if got
2295 ;; display system shows only first of subsequent adjacent
2296 ;; `eq'-identical repeats - use copies to avoid this problem.
2297 (allout-widgets-copy-list (cadr got))
2298 (while (and types (not got))
2299 (setq got
2300 (allout-find-image
2301 (list (append (list :type (car types)
2302 :file (concat use-dir
2303 (symbol-name name)
2304 "." (symbol-name
2305 (car types))))
2306 (if (featurep 'xemacs)
2307 allout-widgets-item-image-properties-xemacs
2308 allout-widgets-item-image-properties-emacs)
2309 ))))
2310 (setq types (cdr types)))
2311 (if got
2312 (push (list key got) allout-widgets-icons-cache))
2313 got)))
2314
2315;;;_ : Miscellaneous
2316;;;_ > allout-elapsed-time-seconds (triple)
2317(defun allout-elapsed-time-seconds (end start)
2318 "Return seconds between `current-time' style time START/END triples."
2319 (let ((elapsed (time-subtract end start)))
2320 (+ (* (car elapsed) (expt 2.0 16))
2321 (cadr elapsed)
2322 (/ (caddr elapsed) (expt 10.0 6)))))
2323;;;_ > allout-frame-property (frame property)
2324(defalias 'allout-frame-property
2325 (cond ((fboundp 'frame-parameter)
2326 'frame-parameter)
2327 ((fboundp 'frame-property)
2328 'frame-property)
2329 (t nil)))
2330;;;_ > allout-find-image (specs)
2331(defalias 'allout-find-image
2332 (if (fboundp 'find-image)
2333 'find-image
2334 nil) ; aka, not-yet-implemented for xemacs.
2335)
2336;;;_ > allout-widgets-copy-list (list)
2337(defun allout-widgets-copy-list (list)
2338 ;; duplicated from cl.el 'copy-list' as of 2008-08-17
2339 "Return a copy of LIST, which may be a dotted list.
2340The elements of LIST are not copied, just the list structure itself."
2341 (if (consp list)
2342 (let ((res nil))
2343 (while (consp list) (push (pop list) res))
2344 (prog1 (nreverse res) (setcdr res list)))
2345 (car list)))
2346
2347;;;_ : Run unit tests:
2348(defun allout-widgets-run-unit-tests ()
2349 (message "Running allout-widget tests...")
2350
2351 (allout-test-range-overlaps)
2352
2353 (message "Running allout-widget tests... Done.")
2354 (sit-for .5))
2355
2356(when allout-widgets-run-unit-tests-on-load
2357 (allout-widgets-run-unit-tests))
2358
2359;;;_ : provide
2360(provide 'allout-widgets)
2361
2362;;;_. Local emacs vars.
2363;;;_ , Local variables:
2364;;;_ , allout-layout: (-1 : 0)
2365;;;_ , End:
diff --git a/lisp/allout.el b/lisp/allout.el
index 5d87415a57f..1a7d8cb1593 100644
--- a/lisp/allout.el
+++ b/lisp/allout.el
@@ -6,7 +6,7 @@
6;; Maintainer: Ken Manheimer <ken dot manheimer at gmail dot com> 6;; Maintainer: Ken Manheimer <ken dot manheimer at gmail dot com>
7;; Created: Dec 1991 -- first release to usenet 7;; Created: Dec 1991 -- first release to usenet
8;; Version: 2.3 8;; Version: 2.3
9;; Keywords: outlines wp languages 9;; Keywords: outlines, wp, languages, PGP, GnuPG
10;; Website: http://myriadicity.net/Sundry/EmacsAllout 10;; Website: http://myriadicity.net/Sundry/EmacsAllout
11 11
12;; This file is part of GNU Emacs. 12;; This file is part of GNU Emacs.
@@ -39,11 +39,9 @@
39;; emacs local file variables need to be enabled when the 39;; emacs local file variables need to be enabled when the
40;; file was visited -- see `enable-local-variables'.) 40;; file was visited -- see `enable-local-variables'.)
41;; - Configurable per-file initial exposure settings 41;; - Configurable per-file initial exposure settings
42;; - Symmetric-key and key-pair topic encryption, plus symmetric passphrase 42;; - Symmetric-key and key-pair topic encryption. Encryption is via the
43;; mnemonic support, with verification against an established passphrase 43;; Emacs 'epg' library. See allout-toggle-current-subtree-encryption
44;; (using a stashed encrypted dummy string) and user-supplied hint 44;; docstring.
45;; maintenance. Encryption is via the Emacs 'epg' library. See
46;; allout-toggle-current-subtree-encryption docstring.
47;; - Automatic topic-number maintenance 45;; - Automatic topic-number maintenance
48;; - "Hot-spot" operation, for single-keystroke maneuvering and 46;; - "Hot-spot" operation, for single-keystroke maneuvering and
49;; exposure control (see the allout-mode docstring) 47;; exposure control (see the allout-mode docstring)
@@ -59,8 +57,8 @@
59;; See the `allout-mode' function's docstring for an introduction to the 57;; See the `allout-mode' function's docstring for an introduction to the
60;; mode. 58;; mode.
61;; 59;;
62;; The latest development version and helpful notes are available at 60;; Directions to the latest development version and helpful notes are
63;; http://myriadicity.net/Sundry/EmacsAllout . 61;; available at http://myriadicity.net/Sundry/EmacsAllout .
64;; 62;;
65;; The outline menubar additions provide quick reference to many of the 63;; The outline menubar additions provide quick reference to many of the
66;; features. See the docstring of the variables `allout-layout' and 64;; features. See the docstring of the variables `allout-layout' and
@@ -76,7 +74,7 @@
76 74
77;;; Code: 75;;; Code:
78 76
79;;;_* Dependency autoloads 77;;;_* Dependency loads
80(require 'overlay) 78(require 'overlay)
81(eval-when-compile 79(eval-when-compile
82 ;; Most of the requires here are for stuff covered by autoloads, which 80 ;; Most of the requires here are for stuff covered by autoloads, which
@@ -94,7 +92,9 @@
94 92
95;;;_ > defgroup allout, allout-keybindings 93;;;_ > defgroup allout, allout-keybindings
96(defgroup allout nil 94(defgroup allout nil
97 "Extensive outline mode for use alone and with other modes." 95 "Extensive outline minor-mode, for use stand-alone and with other modes.
96
97See Allout Auto Activation for automatic activation."
98 :prefix "allout-" 98 :prefix "allout-"
99 :group 'outlines) 99 :group 'outlines)
100(defgroup allout-keybindings nil 100(defgroup allout-keybindings nil
@@ -308,9 +308,7 @@ performing auto-layout is asked of the user each time.
308With value \"activate\", only auto-mode-activation is enabled. 308With value \"activate\", only auto-mode-activation is enabled.
309Auto-layout is not. 309Auto-layout is not.
310 310
311With value nil, neither auto-mode-activation nor auto-layout are 311With value nil, inhibit any automatic allout-mode activation."
312enabled, and allout auto-activation processing is removed from
313file visiting activities."
314 :set 'allout-auto-activation-helper 312 :set 'allout-auto-activation-helper
315 :type '(choice (const :tag "On" t) 313 :type '(choice (const :tag "On" t)
316 (const :tag "Ask about layout" "ask") 314 (const :tag "Ask about layout" "ask")
@@ -752,8 +750,10 @@ Set this var to the bullet you want to use for file cross-references."
752;;;###autoload 750;;;###autoload
753(put 'allout-presentation-padding 'safe-local-variable 'integerp) 751(put 'allout-presentation-padding 'safe-local-variable 'integerp)
754 752
755;;;_ = allout-abbreviate-flattened-numbering 753;;;_ = allout-flattened-numbering-abbreviation
756(defcustom allout-abbreviate-flattened-numbering nil 754(define-obsolete-variable-alias 'allout-abbreviate-flattened-numbering
755 'allout-flattened-numbering-abbreviation "24.0")
756(defcustom allout-flattened-numbering-abbreviation nil
757 "If non-nil, `allout-flatten-exposed-to-buffer' abbreviates topic 757 "If non-nil, `allout-flatten-exposed-to-buffer' abbreviates topic
758numbers to minimal amount with some context. Otherwise, entire 758numbers to minimal amount with some context. Otherwise, entire
759numbers are always used." 759numbers are always used."
@@ -1553,6 +1553,7 @@ See `allout-encryption-ciphertext-rejection-regexps' for rejection reasons.")
1553;;;_ > allout-mode-p () 1553;;;_ > allout-mode-p ()
1554;; Must define this macro above any uses, or byte compilation will lack 1554;; Must define this macro above any uses, or byte compilation will lack
1555;; proper def, if file isn't loaded -- eg, during emacs build! 1555;; proper def, if file isn't loaded -- eg, during emacs build!
1556;;;###autoload
1556(defmacro allout-mode-p () 1557(defmacro allout-mode-p ()
1557 "Return t if `allout-mode' is active in current buffer." 1558 "Return t if `allout-mode' is active in current buffer."
1558 'allout-mode) 1559 'allout-mode)
@@ -5410,7 +5411,7 @@ header and body. The elements of that list are:
5410 bullet))) 5411 bullet)))
5411 (cond ((listp format) 5412 (cond ((listp format)
5412 (list depth 5413 (list depth
5413 (if allout-abbreviate-flattened-numbering 5414 (if allout-flattened-numbering-abbreviation
5414 (allout-stringify-flat-index format 5415 (allout-stringify-flat-index format
5415 gone-out) 5416 gone-out)
5416 (allout-stringify-flat-index-plain 5417 (allout-stringify-flat-index-plain
@@ -6054,7 +6055,7 @@ signal."
6054 (with-temp-buffer 6055 (with-temp-buffer
6055 (insert text) 6056 (insert text)
6056 ;; convey the text characteristics of the original buffer: 6057 ;; convey the text characteristics of the original buffer:
6057 (allout-set-buffer-multibyte multibyte) 6058 (set-buffer-multibyte multibyte)
6058 (when encoding 6059 (when encoding
6059 (set-buffer-file-coding-system encoding) 6060 (set-buffer-file-coding-system encoding)
6060 (if (not decrypt) 6061 (if (not decrypt)
@@ -6085,9 +6086,14 @@ signal."
6085 6086
6086 (setq result-text 6087 (setq result-text
6087 (if decrypt 6088 (if decrypt
6088 (epg-decrypt-string epg-context 6089 (condition-case err
6089 (encode-coding-string massaged-text 6090 (epg-decrypt-string epg-context
6090 (or encoding 'utf-8))) 6091 (encode-coding-string massaged-text
6092 (or encoding 'utf-8)))
6093 (epg-error
6094 (signal 'egp-error
6095 (cons (concat (cadr err) " - gpg version problem?")
6096 (cddr err)))))
6091 (replace-regexp-in-string "\n$" "" 6097 (replace-regexp-in-string "\n$" ""
6092 (epg-encrypt-string epg-context 6098 (epg-encrypt-string epg-context
6093 (encode-coding-string massaged-text 6099 (encode-coding-string massaged-text
@@ -6673,14 +6679,6 @@ To ignore intangibility, bind `inhibit-point-motion-hooks' to t."
6673 'previous-single-property-change) 6679 'previous-single-property-change)
6674 ;; No docstring because xemacs defalias doesn't support it. 6680 ;; No docstring because xemacs defalias doesn't support it.
6675 ) 6681 )
6676;;;_ > allout-set-buffer-multibyte
6677(if (fboundp 'set-buffer-multibyte)
6678 (defalias 'allout-set-buffer-multibyte 'set-buffer-multibyte)
6679 (with-no-warnings
6680 ;; this definition is used only in older or alternative emacs, where
6681 ;; the setting is our only recourse.
6682 (defun allout-set-buffer-multibyte (is-multibyte)
6683 (set enable-multibyte-characters is-multibyte))))
6684;;;_ > allout-select-safe-coding-system 6682;;;_ > allout-select-safe-coding-system
6685(defalias 'allout-select-safe-coding-system 6683(defalias 'allout-select-safe-coding-system
6686 (if (fboundp 'select-safe-coding-system) 6684 (if (fboundp 'select-safe-coding-system)
diff --git a/lisp/dired-x.el b/lisp/dired-x.el
index 95381ccdc0c..202b4e754d7 100644
--- a/lisp/dired-x.el
+++ b/lisp/dired-x.el
@@ -32,7 +32,7 @@
32;; 32;;
33;; (add-hook 'dired-load-hook 33;; (add-hook 'dired-load-hook
34;; (lambda () 34;; (lambda ()
35;; (require 'dired-x) 35;; (load "dired-x")
36;; ;; Set global variables here. For example: 36;; ;; Set global variables here. For example:
37;; ;; (setq dired-guess-shell-gnutar "gtar") 37;; ;; (setq dired-guess-shell-gnutar "gtar")
38;; )) 38;; ))
@@ -79,7 +79,6 @@
79 79
80(defcustom dired-bind-vm nil 80(defcustom dired-bind-vm nil
81 "Non-nil means \"V\" runs `dired-vm', otherwise \"V\" runs `dired-rmail'. 81 "Non-nil means \"V\" runs `dired-vm', otherwise \"V\" runs `dired-rmail'.
82
83RMAIL files in the old Babyl format (used before before Emacs 23.1) 82RMAIL files in the old Babyl format (used before before Emacs 23.1)
84contain \"-*- rmail -*-\" at the top, so `dired-find-file' 83contain \"-*- rmail -*-\" at the top, so `dired-find-file'
85will run `rmail' on these files. New RMAIL files use the standard 84will run `rmail' on these files. New RMAIL files use the standard
@@ -88,26 +87,49 @@ mbox format, and so cannot be distinguished in this way."
88 :group 'dired-keys) 87 :group 'dired-keys)
89 88
90(defcustom dired-bind-jump t 89(defcustom dired-bind-jump t
91 "Non-nil means bind `dired-jump' to C-x C-j, otherwise do not." 90 "Non-nil means bind `dired-jump' to C-x C-j, otherwise do not.
91Setting this variable directly after dired-x is loaded has no effect -
92use \\[customize]."
92 :type 'boolean 93 :type 'boolean
94 :set (lambda (sym val)
95 (if (set sym val)
96 (progn
97 (define-key global-map "\C-x\C-j" 'dired-jump)
98 (define-key global-map "\C-x4\C-j" 'dired-jump-other-window))
99 (if (eq 'dired-jump (lookup-key global-map "\C-x\C-j"))
100 (define-key global-map "\C-x\C-j" nil))
101 (if (eq 'dired-jump-other-window (lookup-key global-map "\C-x4\C-j"))
102 (define-key global-map "\C-x4\C-j" nil))))
93 :group 'dired-keys) 103 :group 'dired-keys)
94 104
95(defcustom dired-bind-man t 105(defcustom dired-bind-man t
96 "Non-nil means bind `dired-man' to \"N\" in dired-mode, otherwise do not." 106 "Non-nil means bind `dired-man' to \"N\" in dired-mode, otherwise do not.
107Setting this variable directly after dired-x is loaded has no effect -
108use \\[customize]."
97 :type 'boolean 109 :type 'boolean
110 :set (lambda (sym val)
111 (if (set sym val)
112 (define-key dired-mode-map "N" 'dired-man)
113 (if (eq 'dired-man (lookup-key dired-mode-map "N"))
114 (define-key dired-mode-map "N" nil))))
98 :group 'dired-keys) 115 :group 'dired-keys)
99 116
100(defcustom dired-bind-info t 117(defcustom dired-bind-info t
101 "Non-nil means bind `dired-info' to \"I\" in dired-mode, otherwise do not." 118 "Non-nil means bind `dired-info' to \"I\" in dired-mode, otherwise do not.
119Setting this variable directly after dired-x is loaded has no effect -
120use \\[customize]."
102 :type 'boolean 121 :type 'boolean
122 :set (lambda (sym val)
123 (if (set sym val)
124 (define-key dired-mode-map "I" 'dired-info)
125 (if (eq 'dired-info (lookup-key dired-mode-map "I"))
126 (define-key dired-mode-map "I" nil))))
103 :group 'dired-keys) 127 :group 'dired-keys)
104 128
105(defcustom dired-vm-read-only-folders nil 129(defcustom dired-vm-read-only-folders nil
106 "If non-nil, \\[dired-vm] will visit all folders read-only. 130 "If non-nil, \\[dired-vm] will visit all folders read-only.
107If neither nil nor t, e.g. the symbol `if-file-read-only', only 131If neither nil nor t, e.g. the symbol `if-file-read-only', only
108files not writable by you are visited read-only. 132files not writable by you are visited read-only."
109
110Read-only folders only work in VM 5, not in VM 4."
111 :type '(choice (const :tag "off" nil) 133 :type '(choice (const :tag "off" nil)
112 (const :tag "on" t) 134 (const :tag "on" t)
113 (other :tag "non-writable only" if-file-read-only)) 135 (other :tag "non-writable only" if-file-read-only))
@@ -181,13 +203,20 @@ listing a directory. See also `dired-local-variables-file'."
181 :type 'boolean 203 :type 'boolean
182 :group 'dired-x) 204 :group 'dired-x)
183 205
184(defcustom dired-guess-shell-gnutar (when (or (eq system-type 'gnu) 206(defcustom dired-guess-shell-gnutar
185 (eq system-type 'gnu/linux)) 207 (catch 'found
186 "tar") 208 (dolist (exe '("tar" "gtar"))
209 (if (with-temp-buffer
210 (ignore-errors (call-process exe nil t nil "--version"))
211 (and (re-search-backward "GNU tar" nil t) t))
212 (throw 'found exe))))
187 "If non-nil, name of GNU tar executable. 213 "If non-nil, name of GNU tar executable.
188\(E.g., \"tar\" or \"gtar\"). The `z' switch will be used with it for 214\(E.g., \"tar\" or \"gtar\"). The `z' switch will be used with it for
189compressed or gzip'ed tar files. If you don't have GNU tar, set this 215compressed or gzip'ed tar files. If you don't have GNU tar, set this
190to nil: a pipe using `zcat' or `gunzip -c' will be used." 216to nil: a pipe using `zcat' or `gunzip -c' will be used."
217 ;; Changed from system-type test to testing --version output.
218 ;; Maybe test --help for -z instead?
219 :version "24.1"
191 :type '(choice (const :tag "Not GNU tar" nil) 220 :type '(choice (const :tag "Not GNU tar" nil)
192 (string :tag "Command name")) 221 (string :tag "Command name"))
193 :group 'dired-x) 222 :group 'dired-x)
@@ -216,19 +245,12 @@ to nil: a pipe using `zcat' or `gunzip -c' will be used."
216(define-key dired-mode-map "*(" 'dired-mark-sexp) 245(define-key dired-mode-map "*(" 'dired-mark-sexp)
217(define-key dired-mode-map "*." 'dired-mark-extension) 246(define-key dired-mode-map "*." 'dired-mark-extension)
218(define-key dired-mode-map "\M-!" 'dired-smart-shell-command) 247(define-key dired-mode-map "\M-!" 'dired-smart-shell-command)
219(define-key dired-mode-map "w" 'dired-copy-filename-as-kill)
220(define-key dired-mode-map "\M-G" 'dired-goto-subdir) 248(define-key dired-mode-map "\M-G" 'dired-goto-subdir)
221(define-key dired-mode-map "F" 'dired-do-find-marked-files) 249(define-key dired-mode-map "F" 'dired-do-find-marked-files)
222(define-key dired-mode-map "Y" 'dired-do-relsymlink) 250(define-key dired-mode-map "Y" 'dired-do-relsymlink)
223(define-key dired-mode-map "%Y" 'dired-do-relsymlink-regexp) 251(define-key dired-mode-map "%Y" 'dired-do-relsymlink-regexp)
224(define-key dired-mode-map "V" 'dired-do-run-mail) 252(define-key dired-mode-map "V" 'dired-do-run-mail)
225 253
226(if dired-bind-man
227 (define-key dired-mode-map "N" 'dired-man))
228
229(if dired-bind-info
230 (define-key dired-mode-map "I" 'dired-info))
231
232;;; MENU BINDINGS 254;;; MENU BINDINGS
233 255
234(require 'easymenu) 256(require 'easymenu)
@@ -270,11 +292,6 @@ matching regexp"]
270files"] 292files"]
271 "Refresh")) 293 "Refresh"))
272 294
273;;; GLOBAL BINDING.
274(when dired-bind-jump
275 (define-key global-map "\C-x\C-j" 'dired-jump)
276 (define-key global-map "\C-x4\C-j" 'dired-jump-other-window))
277
278 295
279;; Install into appropriate hooks. 296;; Install into appropriate hooks.
280 297
@@ -290,31 +307,9 @@ files"]
290 \\[dired-do-find-marked-files]\t-- visit all marked files simultaneously 307 \\[dired-do-find-marked-files]\t-- visit all marked files simultaneously
291 \\[dired-omit-mode]\t-- toggle omitting of files 308 \\[dired-omit-mode]\t-- toggle omitting of files
292 \\[dired-mark-sexp]\t-- mark by Lisp expression 309 \\[dired-mark-sexp]\t-- mark by Lisp expression
293 \\[dired-copy-filename-as-kill]\t-- copy the file or subdir names into the kill ring;
294 \t you can feed it to other commands using \\[yank]
295
296For more features, see variables
297
298 `dired-bind-vm'
299 `dired-bind-jump'
300 `dired-bind-info'
301 `dired-bind-man'
302 `dired-vm-read-only-folders'
303 `dired-omit-mode'
304 `dired-omit-files'
305 `dired-omit-extensions'
306 `dired-omit-size-limit'
307 `dired-find-subdir'
308 `dired-enable-local-variables'
309 `dired-local-variables-file'
310 `dired-guess-shell-gnutar'
311 `dired-guess-shell-gzip-quiet'
312 `dired-guess-shell-znew-switches'
313 `dired-guess-shell-alist-user'
314 `dired-clean-up-buffers-too'
315
316See also functions
317 310
311To see the options you can set, use M-x customize-group RET dired-x RET.
312See also the functions:
318 `dired-flag-extension' 313 `dired-flag-extension'
319 `dired-virtual' 314 `dired-virtual'
320 `dired-jump' 315 `dired-jump'
@@ -324,7 +319,6 @@ See also functions
324 `dired-info' 319 `dired-info'
325 `dired-do-find-marked-files'" 320 `dired-do-find-marked-files'"
326 (interactive) 321 (interactive)
327
328 ;; These must be done in each new dired buffer. 322 ;; These must be done in each new dired buffer.
329 (dired-hack-local-variables) 323 (dired-hack-local-variables)
330 (dired-omit-startup)) 324 (dired-omit-startup))
@@ -339,28 +333,21 @@ Remove expanded subdir of deleted dir, if any."
339 (save-excursion (and (cdr dired-subdir-alist) 333 (save-excursion (and (cdr dired-subdir-alist)
340 (dired-goto-subdir fn) 334 (dired-goto-subdir fn)
341 (dired-kill-subdir))) 335 (dired-kill-subdir)))
342
343 ;; Offer to kill buffer of deleted file FN. 336 ;; Offer to kill buffer of deleted file FN.
344 (if dired-clean-up-buffers-too 337 (when dired-clean-up-buffers-too
345 (progn 338 (let ((buf (get-file-buffer fn)))
346 (let ((buf (get-file-buffer fn))) 339 (and buf
347 (and buf 340 (funcall (function y-or-n-p)
348 (funcall (function y-or-n-p) 341 (format "Kill buffer of %s, too? "
349 (format "Kill buffer of %s, too? " 342 (file-name-nondirectory fn)))
350 (file-name-nondirectory fn))) 343 (kill-buffer buf)))
351 (save-excursion ; you never know where kill-buffer leaves you 344 (let ((buf-list (dired-buffers-for-dir (expand-file-name fn))))
352 (kill-buffer buf)))) 345 (and buf-list
353 (let ((buf-list (dired-buffers-for-dir (expand-file-name fn))) 346 (y-or-n-p (format "Kill dired buffer%s of %s, too? "
354 (buf nil)) 347 (dired-plural-s (length buf-list))
355 (and buf-list 348 (file-name-nondirectory fn)))
356 (y-or-n-p (format "Kill dired buffer%s of %s, too? " 349 (dolist (buf buf-list)
357 (dired-plural-s (length buf-list)) 350 (kill-buffer buf))))))
358 (file-name-nondirectory fn)))
359 (while buf-list
360 (save-excursion (kill-buffer (car buf-list)))
361 (setq buf-list (cdr buf-list)))))))
362 ;; Anything else?
363 )
364 351
365 352
366;;; EXTENSION MARKING FUNCTIONS. 353;;; EXTENSION MARKING FUNCTIONS.
@@ -460,11 +447,10 @@ move to its line in dired."
460 (progn 447 (progn
461 (setq dir (dired-current-directory)) 448 (setq dir (dired-current-directory))
462 (dired-up-directory other-window) 449 (dired-up-directory other-window)
463 (or (dired-goto-file dir) 450 (unless (dired-goto-file dir)
464 ;; refresh and try again 451 ;; refresh and try again
465 (progn 452 (dired-insert-subdir (file-name-directory dir))
466 (dired-insert-subdir (file-name-directory dir)) 453 (dired-goto-file dir)))
467 (dired-goto-file dir))))
468 (if other-window 454 (if other-window
469 (dired-other-window dir) 455 (dired-other-window dir)
470 (dired dir)) 456 (dired dir))
@@ -475,10 +461,9 @@ move to its line in dired."
475 (dired-insert-subdir (file-name-directory file)) 461 (dired-insert-subdir (file-name-directory file))
476 (dired-goto-file file)) 462 (dired-goto-file file))
477 ;; Toggle omitting, if it is on, and try again. 463 ;; Toggle omitting, if it is on, and try again.
478 (if dired-omit-mode 464 (when dired-omit-mode
479 (progn 465 (dired-omit-mode)
480 (dired-omit-mode) 466 (dired-goto-file file)))))))
481 (dired-goto-file file))))))))
482 467
483(defun dired-jump-other-window (&optional file-name) 468(defun dired-jump-other-window (&optional file-name)
484 "Like \\[dired-jump] (`dired-jump') but in other window." 469 "Like \\[dired-jump] (`dired-jump') but in other window."
@@ -695,7 +680,7 @@ you can relist single subdirs using \\[dired-do-redisplay]."
695 (forward-line 1) 680 (forward-line 1)
696 (and (looking-at "^ wildcard ") 681 (and (looking-at "^ wildcard ")
697 (buffer-substring (match-end 0) 682 (buffer-substring (match-end 0)
698 (progn (end-of-line) (point))))))) 683 (line-end-position))))))
699 (if wildcard 684 (if wildcard
700 (setq dirname (expand-file-name wildcard default-directory)))) 685 (setq dirname (expand-file-name wildcard default-directory))))
701 ;; If raw ls listing (not a saved old dired buffer), give it a 686 ;; If raw ls listing (not a saved old dired buffer), give it a
@@ -777,9 +762,12 @@ Also useful for `auto-mode-alist' like this:
777;; mechanism is provided for special handling of the working directory in 762;; mechanism is provided for special handling of the working directory in
778;; special major modes. 763;; special major modes.
779 764
765(define-obsolete-variable-alias 'default-directory-alist
766 'dired-default-directory-alist "24.1")
767
780;; It's easier to add to this alist than redefine function 768;; It's easier to add to this alist than redefine function
781;; default-directory while keeping the old information. 769;; default-directory while keeping the old information.
782(defconst default-directory-alist 770(defconst dired-default-directory-alist
783 '((dired-mode . (if (fboundp 'dired-current-directory) 771 '((dired-mode . (if (fboundp 'dired-current-directory)
784 (dired-current-directory) 772 (dired-current-directory)
785 default-directory))) 773 default-directory)))
@@ -789,8 +777,8 @@ nil is ignored in favor of `default-directory'.")
789 777
790(defun dired-default-directory () 778(defun dired-default-directory ()
791 "Usage like variable `default-directory'. 779 "Usage like variable `default-directory'.
792Knows about the special cases in variable `default-directory-alist'." 780Knows about the special cases in variable `dired-default-directory-alist'."
793 (or (eval (cdr (assq major-mode default-directory-alist))) 781 (or (eval (cdr (assq major-mode dired-default-directory-alist)))
794 default-directory)) 782 default-directory))
795 783
796(defun dired-smart-shell-command (command &optional output-buffer error-buffer) 784(defun dired-smart-shell-command (command &optional output-buffer error-buffer)
@@ -1369,8 +1357,9 @@ NOSELECT the files are merely found but not selected."
1369(declare-function Man-getpage-in-background "man" (topic)) 1357(declare-function Man-getpage-in-background "man" (topic))
1370 1358
1371(defun dired-man () 1359(defun dired-man ()
1372 "Run man on this file. Display old buffer if buffer name matches filename. 1360 "Run `man' on this file."
1373Uses `man.el' of \\[manual-entry] fame." 1361;; Used also to say: "Display old buffer if buffer name matches filename."
1362;; but I have no idea what that means.
1374 (interactive) 1363 (interactive)
1375 (require 'man) 1364 (require 'man)
1376 (let* ((file (dired-get-filename)) 1365 (let* ((file (dired-get-filename))
@@ -1382,7 +1371,7 @@ Uses `man.el' of \\[manual-entry] fame."
1382;; Run Info on files. 1371;; Run Info on files.
1383 1372
1384(defun dired-info () 1373(defun dired-info ()
1385 "Run info on this file." 1374 "Run `info' on this file."
1386 (interactive) 1375 (interactive)
1387 (info (dired-get-filename))) 1376 (info (dired-get-filename)))
1388 1377
@@ -1393,17 +1382,16 @@ Uses `man.el' of \\[manual-entry] fame."
1393 1382
1394(defun dired-vm (&optional read-only) 1383(defun dired-vm (&optional read-only)
1395 "Run VM on this file. 1384 "Run VM on this file.
1396With prefix arg, visit folder read-only (this requires at least VM 5). 1385With optional prefix argument, visits the folder read-only.
1397See also variable `dired-vm-read-only-folders'." 1386Otherwise obeys the value of `dired-vm-read-only-folders'."
1398 (interactive "P") 1387 (interactive "P")
1399 (let ((dir (dired-current-directory)) 1388 (let ((dir (dired-current-directory))
1400 (fil (dired-get-filename))) 1389 (fil (dired-get-filename)))
1401 ;; take care to supply 2nd arg only if requested - may still run VM 4! 1390 (vm-visit-folder fil (or read-only
1402 (cond (read-only (vm-visit-folder fil t)) 1391 (eq t dired-vm-read-only-folders)
1403 ((eq t dired-vm-read-only-folders) (vm-visit-folder fil t)) 1392 (and dired-vm-read-only-folders
1404 ((null dired-vm-read-only-folders) (vm-visit-folder fil)) 1393 (not (file-writable-p fil)))))
1405 (t (vm-visit-folder fil (not (file-writable-p fil))))) 1394 ;; So that pressing `v' inside VM does prompt within current directory:
1406 ;; so that pressing `v' inside VM does prompt within current directory:
1407 (set (make-local-variable 'vm-folder-directory) dir))) 1395 (set (make-local-variable 'vm-folder-directory) dir)))
1408 1396
1409(defun dired-rmail () 1397(defun dired-rmail ()
@@ -1412,7 +1400,7 @@ See also variable `dired-vm-read-only-folders'."
1412 (rmail (dired-get-filename))) 1400 (rmail (dired-get-filename)))
1413 1401
1414(defun dired-do-run-mail () 1402(defun dired-do-run-mail ()
1415 "If `dired-bind-vm' is t, then function `dired-vm', otherwise `dired-rmail'." 1403 "If `dired-bind-vm' is non-nil, call `dired-vm', else call `dired-rmail'."
1416 (interactive) 1404 (interactive)
1417 (if dired-bind-vm 1405 (if dired-bind-vm
1418 ;; Read mail folder using vm. 1406 ;; Read mail folder using vm.
@@ -1450,16 +1438,11 @@ See also variable `dired-vm-read-only-folders'."
1450 1438
1451;; This should be a builtin 1439;; This should be a builtin
1452(defun dired-buffer-more-recently-used-p (buffer1 buffer2) 1440(defun dired-buffer-more-recently-used-p (buffer1 buffer2)
1453 "Return t if BUFFER1 is more recently used than BUFFER2." 1441 "Return t if BUFFER1 is more recently used than BUFFER2.
1454 (if (equal buffer1 buffer2) 1442Considers buffers closer to the car of `buffer-list' to be more recent."
1455 nil 1443 (and (not (equal buffer1 buffer2))
1456 (let ((more-recent nil) 1444 (memq buffer1 (buffer-list))
1457 (list (buffer-list))) 1445 (not (memq buffer1 (memq buffer2 (buffer-list))))))
1458 (while (and list
1459 (not (setq more-recent (equal buffer1 (car list))))
1460 (not (equal buffer2 (car list))))
1461 (setq list (cdr list)))
1462 more-recent)))
1463 1446
1464;; Same thing as `dired-buffers-for-dir' of dired.el? - lrd 11/23/93 1447;; Same thing as `dired-buffers-for-dir' of dired.el? - lrd 11/23/93
1465;; (defun dired-buffers-for-dir-exact (dir) 1448;; (defun dired-buffers-for-dir-exact (dir)
@@ -1559,7 +1542,7 @@ to mark all zero length files."
1559 (forward-char mode-len) 1542 (forward-char mode-len)
1560 (setq nlink (read (current-buffer))) 1543 (setq nlink (read (current-buffer)))
1561 ;; Karsten Wenger <kw@cis.uni-muenchen.de> fixed uid. 1544 ;; Karsten Wenger <kw@cis.uni-muenchen.de> fixed uid.
1562 (setq uid (buffer-substring (+ (point) 1) 1545 (setq uid (buffer-substring (1+ (point))
1563 (progn (forward-word 1) (point)))) 1546 (progn (forward-word 1) (point))))
1564 (re-search-forward directory-listing-before-filename-regexp) 1547 (re-search-forward directory-listing-before-filename-regexp)
1565 (goto-char (match-beginning 1)) 1548 (goto-char (match-beginning 1))
@@ -1649,7 +1632,7 @@ Identical to `find-file' except when called interactively, with a prefix arg
1649\(e.g., \\[universal-argument]\), in which case it guesses filename near point. 1632\(e.g., \\[universal-argument]\), in which case it guesses filename near point.
1650Useful for editing file mentioned in buffer you are viewing, 1633Useful for editing file mentioned in buffer you are viewing,
1651or to test if that file exists. Use minibuffer after snatching filename." 1634or to test if that file exists. Use minibuffer after snatching filename."
1652 (interactive (list (read-filename-at-point "Find file: "))) 1635 (interactive (list (dired-x-read-filename-at-point "Find file: ")))
1653 (find-file (expand-file-name filename))) 1636 (find-file (expand-file-name filename)))
1654 1637
1655(defun dired-x-find-file-other-window (filename) 1638(defun dired-x-find-file-other-window (filename)
@@ -1661,52 +1644,43 @@ Identical to `find-file-other-window' except when called interactively, with
1661a prefix arg \(e.g., \\[universal-argument]\), in which case it guesses filename near point. 1644a prefix arg \(e.g., \\[universal-argument]\), in which case it guesses filename near point.
1662Useful for editing file mentioned in buffer you are viewing, 1645Useful for editing file mentioned in buffer you are viewing,
1663or to test if that file exists. Use minibuffer after snatching filename." 1646or to test if that file exists. Use minibuffer after snatching filename."
1664 (interactive (list (read-filename-at-point "Find file: "))) 1647 (interactive (list (dired-x-read-filename-at-point "Find file: ")))
1665 (find-file-other-window (expand-file-name filename))) 1648 (find-file-other-window (expand-file-name filename)))
1666 1649
1667;;; Internal functions. 1650;;; Internal functions.
1668 1651
1669;; Fixme: This should probably use `thing-at-point'. -- fx 1652;; Fixme: This should probably use `thing-at-point'. -- fx
1670(defun dired-filename-at-point () 1653(defun dired-filename-at-point ()
1671 "Get the filename closest to point, but do not change position. 1654 "Return the filename closest to point, expanded.
1672Has a preference for looking backward when not directly on a symbol. 1655Point should be in or after a filename."
1673Not perfect - point must be in middle of or end of filename."
1674
1675 (let ((filename-chars "-.[:alnum:]_/:$+@") 1656 (let ((filename-chars "-.[:alnum:]_/:$+@")
1676 start end filename prefix) 1657 start end filename prefix)
1677
1678 (save-excursion 1658 (save-excursion
1679 ;; First see if just past a filename. 1659 ;; First see if just past a filename.
1680 (if (not (eobp)) 1660 (or (eobp) ; why?
1681 (if (looking-at "[] \t\n[{}()]") ; whitespace or some parens 1661 (when (looking-at "[] \t\n[{}()]") ; whitespace or some parens
1682 (progn 1662 (skip-chars-backward " \n\t\r({[]})")
1683 (skip-chars-backward " \n\t\r({[]})") 1663 (or (bobp) (backward-char 1))))
1684 (if (not (bobp)) 1664 (if (looking-at (format "[%s]" filename-chars))
1685 (backward-char 1)))))
1686
1687 (if (string-match (concat "[" filename-chars "]")
1688 (char-to-string (following-char)))
1689 (progn 1665 (progn
1690 (if (re-search-backward (concat "[^" filename-chars "]") nil t) 1666 (skip-chars-backward filename-chars)
1691 (forward-char) 1667 (setq start (point)
1692 (goto-char (point-min))) 1668 prefix
1693 (setq start (point)) 1669 ;; This is something to do with ange-ftp filenames.
1694 (setq prefix 1670 ;; It convert foo@bar to /foo@bar.
1671 ;; But when does the former occur in dired buffers?
1695 (and (string-match 1672 (and (string-match
1696 "^\\w+@" 1673 "^\\w+@"
1697 (buffer-substring start (line-beginning-position))) 1674 (buffer-substring start (line-end-position)))
1698 "/")) 1675 "/"))
1699 (goto-char start)
1700 (if (string-match "[/~]" (char-to-string (preceding-char))) 1676 (if (string-match "[/~]" (char-to-string (preceding-char)))
1701 (setq start (1- start))) 1677 (setq start (1- start)))
1702 (re-search-forward (concat "\\=[" filename-chars "]*") nil t)) 1678 (skip-chars-forward filename-chars))
1703
1704 (error "No file found around point!")) 1679 (error "No file found around point!"))
1705
1706 ;; Return string. 1680 ;; Return string.
1707 (expand-file-name (concat prefix (buffer-substring start (point))))))) 1681 (expand-file-name (concat prefix (buffer-substring start (point)))))))
1708 1682
1709(defun read-filename-at-point (prompt) 1683(defun dired-x-read-filename-at-point (prompt)
1710 "Return filename prompting with PROMPT with completion. 1684 "Return filename prompting with PROMPT with completion.
1711If `current-prefix-arg' is non-nil, uses name at point as guess." 1685If `current-prefix-arg' is non-nil, uses name at point as guess."
1712 (if current-prefix-arg 1686 (if current-prefix-arg
@@ -1716,6 +1690,9 @@ If `current-prefix-arg' is non-nil, uses name at point as guess."
1716 guess 1690 guess
1717 nil (file-name-nondirectory guess))) 1691 nil (file-name-nondirectory guess)))
1718 (read-file-name prompt default-directory))) 1692 (read-file-name prompt default-directory)))
1693
1694(define-obsolete-function-alias 'read-filename-at-point
1695 'dired-x-read-filename-at-point "24.1") ; is this even needed?
1719 1696
1720;;; BUG REPORTS 1697;;; BUG REPORTS
1721 1698
diff --git a/lisp/dired.el b/lisp/dired.el
index 058dbdc548a..22d9f91648c 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -4029,7 +4029,7 @@ true then the type of the file linked to by FILE is printed instead.
4029;;;*** 4029;;;***
4030 4030
4031;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el" 4031;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el"
4032;;;;;; "d35468f85920d324895b0c04bb703328") 4032;;;;;; "a2af6147cf06b53166d9e1a3bb200675")
4033;;; Generated autoloads from dired-x.el 4033;;; Generated autoloads from dired-x.el
4034 4034
4035(autoload 'dired-jump "dired-x" "\ 4035(autoload 'dired-jump "dired-x" "\
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 695dc1e2db6..b3c95fcc78f 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -1877,6 +1877,7 @@ BUFFER-NAME, if non-nil, is the buffer name to use."
1877 (let ((inhibit-read-only t)) 1877 (let ((inhibit-read-only t))
1878 (buffer-disable-undo) 1878 (buffer-disable-undo)
1879 (erase-buffer) 1879 (erase-buffer)
1880 (ert-results-mode)
1880 ;; Erase buffer again in case switching out of the previous 1881 ;; Erase buffer again in case switching out of the previous
1881 ;; mode inserted anything. (This happens e.g. when switching 1882 ;; mode inserted anything. (This happens e.g. when switching
1882 ;; from ert-results-mode to ert-results-mode when 1883 ;; from ert-results-mode to ert-results-mode when
@@ -1895,9 +1896,8 @@ BUFFER-NAME, if non-nil, is the buffer name to use."
1895 (ewoc-enter-last ewoc 1896 (ewoc-enter-last ewoc
1896 (make-ert--ewoc-entry :test test :hidden-p t))) 1897 (make-ert--ewoc-entry :test test :hidden-p t)))
1897 (ert--results-update-ewoc-hf ert--results-ewoc ert--results-stats) 1898 (ert--results-update-ewoc-hf ert--results-ewoc ert--results-stats)
1898 (goto-char (1- (point-max))))) 1899 (goto-char (1- (point-max)))
1899 (ert-results-mode) 1900 buffer)))))
1900 buffer)))
1901 1901
1902 1902
1903(defvar ert--selector-history nil 1903(defvar ert--selector-history nil
@@ -2343,6 +2343,7 @@ To be used in the ERT results buffer."
2343 (let ((inhibit-read-only t)) 2343 (let ((inhibit-read-only t))
2344 (buffer-disable-undo) 2344 (buffer-disable-undo)
2345 (erase-buffer) 2345 (erase-buffer)
2346 (ert-simple-view-mode)
2346 ;; Use unibyte because `debugger-setup-buffer' also does so. 2347 ;; Use unibyte because `debugger-setup-buffer' also does so.
2347 (set-buffer-multibyte nil) 2348 (set-buffer-multibyte nil)
2348 (setq truncate-lines t) 2349 (setq truncate-lines t)
@@ -2351,8 +2352,7 @@ To be used in the ERT results buffer."
2351 (goto-char (point-min)) 2352 (goto-char (point-min))
2352 (insert "Backtrace for test `") 2353 (insert "Backtrace for test `")
2353 (ert-insert-test-name-button (ert-test-name test)) 2354 (ert-insert-test-name-button (ert-test-name test))
2354 (insert "':\n") 2355 (insert "':\n")))))))
2355 (ert-simple-view-mode)))))))
2356 2356
2357(defun ert-results-pop-to-messages-for-test-at-point () 2357(defun ert-results-pop-to-messages-for-test-at-point ()
2358 "Display the part of the *Messages* buffer generated during the test at point. 2358 "Display the part of the *Messages* buffer generated during the test at point.
@@ -2368,12 +2368,12 @@ To be used in the ERT results buffer."
2368 (let ((inhibit-read-only t)) 2368 (let ((inhibit-read-only t))
2369 (buffer-disable-undo) 2369 (buffer-disable-undo)
2370 (erase-buffer) 2370 (erase-buffer)
2371 (ert-simple-view-mode)
2371 (insert (ert-test-result-messages result)) 2372 (insert (ert-test-result-messages result))
2372 (goto-char (point-min)) 2373 (goto-char (point-min))
2373 (insert "Messages for test `") 2374 (insert "Messages for test `")
2374 (ert-insert-test-name-button (ert-test-name test)) 2375 (ert-insert-test-name-button (ert-test-name test))
2375 (insert "':\n") 2376 (insert "':\n")))))
2376 (ert-simple-view-mode)))))
2377 2377
2378(defun ert-results-pop-to-should-forms-for-test-at-point () 2378(defun ert-results-pop-to-should-forms-for-test-at-point ()
2379 "Display the list of `should' forms executed during the test at point. 2379 "Display the list of `should' forms executed during the test at point.
@@ -2389,6 +2389,7 @@ To be used in the ERT results buffer."
2389 (let ((inhibit-read-only t)) 2389 (let ((inhibit-read-only t))
2390 (buffer-disable-undo) 2390 (buffer-disable-undo)
2391 (erase-buffer) 2391 (erase-buffer)
2392 (ert-simple-view-mode)
2392 (if (null (ert-test-result-should-forms result)) 2393 (if (null (ert-test-result-should-forms result))
2393 (insert "\n(No should forms during this test.)\n") 2394 (insert "\n(No should forms during this test.)\n")
2394 (loop for form-description in (ert-test-result-should-forms result) 2395 (loop for form-description in (ert-test-result-should-forms result)
@@ -2406,8 +2407,7 @@ To be used in the ERT results buffer."
2406 (insert (concat "(Values are shallow copies and may have " 2407 (insert (concat "(Values are shallow copies and may have "
2407 "looked different during the test if they\n" 2408 "looked different during the test if they\n"
2408 "have been modified destructively.)\n")) 2409 "have been modified destructively.)\n"))
2409 (forward-line 1) 2410 (forward-line 1)))))
2410 (ert-simple-view-mode)))))
2411 2411
2412(defun ert-results-toggle-printer-limits-for-test-at-point () 2412(defun ert-results-toggle-printer-limits-for-test-at-point ()
2413 "Toggle how much of the condition to print for the test at point. 2413 "Toggle how much of the condition to print for the test at point.
@@ -2442,6 +2442,7 @@ To be used in the ERT results buffer."
2442 (let ((inhibit-read-only t)) 2442 (let ((inhibit-read-only t))
2443 (buffer-disable-undo) 2443 (buffer-disable-undo)
2444 (erase-buffer) 2444 (erase-buffer)
2445 (ert-simple-view-mode)
2445 (if (null data) 2446 (if (null data)
2446 (insert "(No data)\n") 2447 (insert "(No data)\n")
2447 (insert (format "%-3s %8s %8s\n" "" "time" "cumul")) 2448 (insert (format "%-3s %8s %8s\n" "" "time" "cumul"))
@@ -2454,8 +2455,7 @@ To be used in the ERT results buffer."
2454 (insert "\n")))) 2455 (insert "\n"))))
2455 (goto-char (point-min)) 2456 (goto-char (point-min))
2456 (insert "Tests by run time (seconds):\n\n") 2457 (insert "Tests by run time (seconds):\n\n")
2457 (forward-line 1) 2458 (forward-line 1))))
2458 (ert-simple-view-mode))))
2459 2459
2460;;;###autoload 2460;;;###autoload
2461(defun ert-describe-test (test-or-test-name) 2461(defun ert-describe-test (test-or-test-name)
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 24ea0a3e801..3179672a3ec 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -61,6 +61,8 @@ UPatterns can take the following forms:
61 `QPAT matches if the QPattern QPAT matches. 61 `QPAT matches if the QPattern QPAT matches.
62 (pred PRED) matches if PRED applied to the object returns non-nil. 62 (pred PRED) matches if PRED applied to the object returns non-nil.
63 (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil. 63 (guard BOOLEXP) matches if BOOLEXP evaluates to non-nil.
64If a SYMBOL is used twice in the same pattern (i.e. the pattern is
65\"non-linear\"), then the second occurrence is turned into an `eq'uality test.
64 66
65QPatterns can take the following forms: 67QPatterns can take the following forms:
66 (QPAT1 . QPAT2) matches if QPAT1 matches the car and QPAT2 the cdr. 68 (QPAT1 . QPAT2) matches if QPAT1 matches the car and QPAT2 the cdr.
@@ -457,7 +459,12 @@ and otherwise defers to REST which is a list of branches of the form
457 (pcase--u1 matches code vars then-rest) 459 (pcase--u1 matches code vars then-rest)
458 (pcase--u else-rest)))) 460 (pcase--u else-rest))))
459 ((symbolp upat) 461 ((symbolp upat)
460 (pcase--u1 matches code (cons (cons upat sym) vars) rest)) 462 (if (not (assq upat vars))
463 (pcase--u1 matches code (cons (cons upat sym) vars) rest)
464 ;; Non-linear pattern. Turn it into an `eq' test.
465 (pcase--u1 (cons `(match ,sym . (pred (eq ,(cdr (assq upat vars)))))
466 matches)
467 code vars rest)))
461 ((eq (car-safe upat) '\`) 468 ((eq (car-safe upat) '\`)
462 (pcase--q1 sym (cadr upat) matches code vars rest)) 469 (pcase--q1 sym (cadr upat) matches code vars rest))
463 ((eq (car-safe upat) 'or) 470 ((eq (car-safe upat) 'or)
diff --git a/lisp/gnus/ChangeLog b/lisp/gnus/ChangeLog
index 91ba9e5a359..b40c6b7d60f 100644
--- a/lisp/gnus/ChangeLog
+++ b/lisp/gnus/ChangeLog
@@ -1,3 +1,81 @@
12011-02-18 Lars Ingebrigtsen <larsi@gnus.org>
2
3 * auth-source.el (auth-source-search): Don't try to create credentials
4 if the caller doesn't want that.
5
6 * nnimap.el (nnimap-log-command): Add a newline to the inhibited
7 logging.
8 (nnimap-credentials): Protect against auth-source-search returning nil.
9 (nnimap-request-list): Protect against not being able to open the
10 server.
11
122011-02-17 Lars Ingebrigtsen <larsi@gnus.org>
13
14 * auth-source.el (auth-source-search): Do a two-phase search, one with
15 no :create to get the responses from all backends.
16
17 * nnimap.el (nnimap-open-connection-1): Delete duplicate server names
18 when getting credentials.
19
20 * gnus-util.el (gnus-delete-duplicates): New function.
21
222011-02-17 Teodor Zlatanov <tzz@lifelogs.com>
23
24 * nnimap.el (nnimap-credentials): Instead of picking the first port as
25 a creation default, pass the whole port list down. It will be
26 completed.
27
28 * auth-source.el (auth-source-search): Updated docs to talk about
29 multiple creation choices.
30 (auth-source-netrc-create): Accept a list as a value (from the search
31 parameters) and do completion on that list. Keep a separate netrc line
32 with the password obscured for showing the user.
33
34 * nnimap.el (nnimap-open-connection-1): Make the `nnimap-address' the
35 first choice to `auth-source-search' so it will be used for entry
36 creation instead of the server's Gnus-specific name.
37 (nnimap-credentials): Rely on the auth-source library to select which
38 port is actually wanted in the new netrc entry, so don't override
39 `auth-source-creation-defaults'.
40
41 * auth-source.el (auth-source-netrc-parse): Use :port instead of
42 :protocol and accept a missing user, host, or port as a wildcard match.
43 (auth-source-debug): Default to off.
44
45 (auth-source-netrc-search, auth-source-netrc-create)
46 (auth-source-secrets-search, auth-source-secrets-create)
47 (auth-source-user-or-password, auth-source-backend, auth-sources)
48 (auth-source-backend-parse-parameters, auth-source-search): Use :port
49 instead of :protocol.
50
51 * nnimap.el (nnimap-credentials): Pass a port default to
52 `auth-source-search' in case an entry needs to be created.
53 (nnimap-open-connection-1): Use :port instead of :protocol.
54
552011-02-17 Katsumi Yamaoka <yamaoka@jpl.org>
56
57 * auth-source.el (auth-source-secrets-search): Use mm-delete-duplicates
58 instead of delete-dups that is not available in XEmacs 21.4.
59
602011-02-16 Lars Ingebrigtsen <larsi@gnus.org>
61
62 * gnus-sum.el (gnus-propagate-marks): Change default to t again, since
63 nil means that nnimap doesn't get updated.
64
652011-02-16 Teodor Zlatanov <tzz@lifelogs.com>
66
67 * auth-source.el (auth-source-netrc-create): Return a synthetic search
68 result when the user doesn't want to write to the file.
69 (auth-source-netrc-search): Expect a synthetic result and proceed
70 accordingly.
71 (auth-source-cache-expiry): New variable to override
72 `password-cache-expiry'.
73 (auth-source-remember): Use it.
74
75 * nnimap.el (nnimap-credentials): Remove the `inhibit-create'
76 parameter. Create entry if necessary by using :create t.
77 (nnimap-open-connection-1): Don't pass `inhibit-create'.
78
12011-02-15 Teodor Zlatanov <tzz@lifelogs.com> 792011-02-15 Teodor Zlatanov <tzz@lifelogs.com>
2 80
3 * auth-source.el (auth-source-debug): Enable by default and don't 81 * auth-source.el (auth-source-debug): Enable by default and don't
diff --git a/lisp/gnus/auth-source.el b/lisp/gnus/auth-source.el
index a259c5c2f0b..4fdf521b1a9 100644
--- a/lisp/gnus/auth-source.el
+++ b/lisp/gnus/auth-source.el
@@ -40,6 +40,7 @@
40;;; Code: 40;;; Code:
41 41
42(require 'password-cache) 42(require 'password-cache)
43(require 'mm-util)
43(require 'gnus-util) 44(require 'gnus-util)
44(require 'netrc) 45(require 'netrc)
45(require 'assoc) 46(require 'assoc)
@@ -61,6 +62,18 @@
61 :version "23.1" ;; No Gnus 62 :version "23.1" ;; No Gnus
62 :group 'gnus) 63 :group 'gnus)
63 64
65;;;###autoload
66(defcustom auth-source-cache-expiry 7200
67 "How many seconds passwords are cached, or nil to disable
68expiring. Overrides `password-cache-expiry' through a
69let-binding."
70 :group 'auth-source
71 :type '(choice (const :tag "Never" nil)
72 (const :tag "All Day" 86400)
73 (const :tag "2 Hours" 7200)
74 (const :tag "30 Minutes" 1800)
75 (integer :tag "Seconds")))
76
64(defclass auth-source-backend () 77(defclass auth-source-backend ()
65 ((type :initarg :type 78 ((type :initarg :type
66 :initform 'netrc 79 :initform 'netrc
@@ -81,11 +94,11 @@
81 :type t 94 :type t
82 :custom string 95 :custom string
83 :documentation "The backend user.") 96 :documentation "The backend user.")
84 (protocol :initarg :protocol 97 (port :initarg :port
85 :initform t 98 :initform t
86 :type t 99 :type t
87 :custom string 100 :custom string
88 :documentation "The backend protocol.") 101 :documentation "The backend protocol.")
89 (create-function :initarg :create-function 102 (create-function :initarg :create-function
90 :initform ignore 103 :initform ignore
91 :type function 104 :type function
@@ -135,7 +148,7 @@
135 :version "23.2" ;; No Gnus 148 :version "23.2" ;; No Gnus
136 :type `boolean) 149 :type `boolean)
137 150
138(defcustom auth-source-debug t 151(defcustom auth-source-debug nil
139 "Whether auth-source should log debug messages. 152 "Whether auth-source should log debug messages.
140 153
141If the value is nil, debug messages are not logged. 154If the value is nil, debug messages are not logged.
@@ -200,7 +213,7 @@ can get pretty complex."
200 :tag "Regular expression"))) 213 :tag "Regular expression")))
201 (list 214 (list
202 :tag "Protocol" 215 :tag "Protocol"
203 (const :format "" :value :protocol) 216 (const :format "" :value :port)
204 (choice 217 (choice
205 :tag "Protocol" 218 :tag "Protocol"
206 (const :tag "Any" t) 219 (const :tag "Any" t)
@@ -253,19 +266,19 @@ If the value is not a list, symmetric encryption will be used."
253 msg)) 266 msg))
254 267
255 268
256;; (auth-source-pick nil :host "any" :protocol 'imap :user "joe") 269;; (auth-source-pick nil :host "any" :port 'imap :user "joe")
257;; (auth-source-pick t :host "any" :protocol 'imap :user "joe") 270;; (auth-source-pick t :host "any" :port 'imap :user "joe")
258;; (setq auth-sources '((:source (:secrets default) :host t :protocol t :user "joe") 271;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
259;; (:source (:secrets "session") :host t :protocol t :user "joe") 272;; (:source (:secrets "session") :host t :port t :user "joe")
260;; (:source (:secrets "Login") :host t :protocol t) 273;; (:source (:secrets "Login") :host t :port t)
261;; (:source "~/.authinfo.gpg" :host t :protocol t))) 274;; (:source "~/.authinfo.gpg" :host t :port t)))
262 275
263;; (setq auth-sources '((:source (:secrets default) :host t :protocol t :user "joe") 276;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
264;; (:source (:secrets "session") :host t :protocol t :user "joe") 277;; (:source (:secrets "session") :host t :port t :user "joe")
265;; (:source (:secrets "Login") :host t :protocol t) 278;; (:source (:secrets "Login") :host t :port t)
266;; )) 279;; ))
267 280
268;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :protocol t))) 281;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :port t)))
269 282
270;; (auth-source-backend-parse "myfile.gpg") 283;; (auth-source-backend-parse "myfile.gpg")
271;; (auth-source-backend-parse 'default) 284;; (auth-source-backend-parse 'default)
@@ -342,8 +355,8 @@ If the value is not a list, symmetric encryption will be used."
342 355
343(defun auth-source-backend-parse-parameters (entry backend) 356(defun auth-source-backend-parse-parameters (entry backend)
344 "Fills in the extra auth-source-backend parameters of ENTRY. 357 "Fills in the extra auth-source-backend parameters of ENTRY.
345Using the plist ENTRY, get the :host, :protocol, and :user search 358Using the plist ENTRY, get the :host, :port, and :user search
346parameters. Accepts :port as an alias to :protocol." 359parameters."
347 (let ((entry (if (stringp entry) 360 (let ((entry (if (stringp entry)
348 nil 361 nil
349 entry)) 362 entry))
@@ -352,15 +365,14 @@ parameters. Accepts :port as an alias to :protocol."
352 (oset backend host val)) 365 (oset backend host val))
353 (when (setq val (plist-get entry :user)) 366 (when (setq val (plist-get entry :user))
354 (oset backend user val)) 367 (oset backend user val))
355 ;; accept :port as an alias for :protocol 368 (when (setq val (plist-get entry :port))
356 (when (setq val (or (plist-get entry :protocol) (plist-get entry :port))) 369 (oset backend port val)))
357 (oset backend protocol val)))
358 backend) 370 backend)
359 371
360;; (mapcar 'auth-source-backend-parse auth-sources) 372;; (mapcar 'auth-source-backend-parse auth-sources)
361 373
362(defun* auth-source-search (&rest spec 374(defun* auth-source-search (&rest spec
363 &key type max host user protocol secret 375 &key type max host user port secret
364 create delete 376 create delete
365 &allow-other-keys) 377 &allow-other-keys)
366 "Search or modify authentication backends according to SPEC. 378 "Search or modify authentication backends according to SPEC.
@@ -373,7 +385,7 @@ other properties will always hold scalar values.
373 385
374Typically the :secret property, if present, contains a password. 386Typically the :secret property, if present, contains a password.
375 387
376Common search keys are :max, :host, :protocol, and :user. In 388Common search keys are :max, :host, :port, and :user. In
377addition, :create specifies how tokens will be or created. 389addition, :create specifies how tokens will be or created.
378Finally, :type can specify which backend types you want to check. 390Finally, :type can specify which backend types you want to check.
379 391
@@ -387,7 +399,7 @@ any of the search terms matches).
387A new token will be created if no matching tokens were found. 399A new token will be created if no matching tokens were found.
388The new token will have only the keys the backend requires. For 400The new token will have only the keys the backend requires. For
389the netrc backend, for instance, that's the user, host, and 401the netrc backend, for instance, that's the user, host, and
390protocol keys. 402port keys.
391 403
392Here's an example: 404Here's an example:
393 405
@@ -403,11 +415,11 @@ which says:
403 'netrc', maximum one result. 415 'netrc', maximum one result.
404 416
405 Create a new entry if you found none. The netrc backend will 417 Create a new entry if you found none. The netrc backend will
406 automatically require host, user, and protocol. The host will be 418 automatically require host, user, and port. The host will be
407 'mine'. We prompt for the user with default 'defaultUser' and 419 'mine'. We prompt for the user with default 'defaultUser' and
408 for the protocol without a default. We will not prompt for A, Q, 420 for the port without a default. We will not prompt for A, Q,
409 or P. The resulting token will only have keys user, host, and 421 or P. The resulting token will only have keys user, host, and
410 protocol.\" 422 port.\"
411 423
412:create '(A B C) also means to create a token if possible. 424:create '(A B C) also means to create a token if possible.
413 425
@@ -432,17 +444,17 @@ which says:
432 or 'twosuch' in backends of type 'netrc', maximum one result. 444 or 'twosuch' in backends of type 'netrc', maximum one result.
433 445
434 Create a new entry if you found none. The netrc backend will 446 Create a new entry if you found none. The netrc backend will
435 automatically require host, user, and protocol. The host will be 447 automatically require host, user, and port. The host will be
436 'nonesuch' and Q will be 'qqqq'. We prompt for A with default 448 'nonesuch' and Q will be 'qqqq'. We prompt for A with default
437 'default A', for B and protocol with default nil, and for the 449 'default A', for B and port with default nil, and for the
438 user with default 'defaultUser'. We will not prompt for Q. The 450 user with default 'defaultUser'. We will not prompt for Q. The
439 resulting token will have keys user, host, protocol, A, B, and Q. 451 resulting token will have keys user, host, port, A, B, and Q.
440 It will not have P with any value, even though P is used in the 452 It will not have P with any value, even though P is used in the
441 search to find only entries that have P set to 'pppp'.\" 453 search to find only entries that have P set to 'pppp'.\"
442 454
443When multiple values are specified in the search parameter, the 455When multiple values are specified in the search parameter, the
444first one is used for creation. So :host (X Y Z) would create a 456user is prompted for which one. So :host (X Y Z) would ask the
445token for host X, for instance. 457user to choose between X, Y, and Z.
446 458
447This creation can fail if the search was not specific enough to 459This creation can fail if the search was not specific enough to
448create a new token (it's up to the backend to decide that). You 460create a new token (it's up to the backend to decide that). You
@@ -468,14 +480,14 @@ the match rules above. Defaults to t.
468:user (X Y Z) means to match only users X, Y, or Z according to 480:user (X Y Z) means to match only users X, Y, or Z according to
469the match rules above. Defaults to t. 481the match rules above. Defaults to t.
470 482
471:protocol (P Q R) means to match only protocols P, Q, or R. 483:port (P Q R) means to match only protocols P, Q, or R.
472Defaults to t. 484Defaults to t.
473 485
474:K (V1 V2 V3) for any other key K will match values V1, V2, or 486:K (V1 V2 V3) for any other key K will match values V1, V2, or
475V3 (note the match rules above). 487V3 (note the match rules above).
476 488
477The return value is a list with at most :max tokens. Each token 489The return value is a list with at most :max tokens. Each token
478is a plist with keys :backend :host :protocol :user, plus any other 490is a plist with keys :backend :host :port :user, plus any other
479keys provided by the backend (notably :secret). But note the 491keys provided by the backend (notably :secret). But note the
480exception for :max 0, which see above. 492exception for :max 0, which see above.
481 493
@@ -488,7 +500,7 @@ must call it to obtain the actual value."
488 unless (memq (nth i spec) ignored-keys) 500 unless (memq (nth i spec) ignored-keys)
489 collect (nth i spec))) 501 collect (nth i spec)))
490 (found (auth-source-recall spec)) 502 (found (auth-source-recall spec))
491 filtered-backends accessor-key found-here goal) 503 filtered-backends accessor-key found-here goal matches)
492 504
493 (if (and found auth-source-do-cache) 505 (if (and found auth-source-do-cache)
494 (auth-source-do-debug 506 (auth-source-do-debug
@@ -517,38 +529,58 @@ must call it to obtain the actual value."
517 529
518 ;; (debug spec "filtered" filtered-backends) 530 ;; (debug spec "filtered" filtered-backends)
519 (setq goal max) 531 (setq goal max)
520 (dolist (backend filtered-backends) 532 ;; First go through all the backends without :create, so we can
521 (setq found-here (apply 533 ;; query them all.
522 (slot-value backend 'search-function) 534 (let ((uspec (copy-sequence spec)))
523 :backend backend 535 (plist-put uspec :create nil)
524 :create create 536 (dolist (backend filtered-backends)
525 :delete delete 537 (let ((match (apply
526 spec)) 538 (slot-value backend 'search-function)
527 539 :backend backend
528 ;; if max is 0, as soon as we find something, return it 540 uspec)))
529 (when (and (zerop max) (> 0 (length found-here))) 541 (when match
530 (return t)) 542 (push (list backend match) matches)))))
531 543 ;; If we didn't find anything, then we allow the backend(s) to
532 ;; decrement the goal by the number of new results 544 ;; create the entries.
533 (decf goal (length found-here)) 545 (when (and create
534 ;; and append the new results to the full list 546 (not matches))
535 (setq found (append found found-here)) 547 (let ((match (apply
536 548 (slot-value backend 'search-function)
537 (auth-source-do-debug 549 :backend backend
538 "auth-source-search: found %d results (max %d/%d) in %S matching %S" 550 :create create
539 (length found-here) max goal backend spec) 551 :delete delete
540 552 spec)))
541 ;; return full list if the goal is 0 or negative 553 (when match
542 (when (zerop (max 0 goal)) 554 (push (list backend match) matches))))
543 (return found)) 555
544 556 (setq backend (caar matches)
545 ;; change the :max parameter in the spec to the goal 557 found-here (cadar matches))
546 (setq spec (plist-put spec :max goal))) 558
547 559 (block nil
548 (when (and found auth-source-do-cache) 560 ;; if max is 0, as soon as we find something, return it
549 (auth-source-remember spec found))) 561 (when (and (zerop max) (> 0 (length found-here)))
550 562 (return t))
551 found)) 563
564 ;; decrement the goal by the number of new results
565 (decf goal (length found-here))
566 ;; and append the new results to the full list
567 (setq found (append found found-here))
568
569 (auth-source-do-debug
570 "auth-source-search: found %d results (max %d/%d) in %S matching %S"
571 (length found-here) max goal backend spec)
572
573 ;; return full list if the goal is 0 or negative
574 (when (zerop (max 0 goal))
575 (return found))
576
577 ;; change the :max parameter in the spec to the goal
578 (setq spec (plist-put spec :max goal))
579
580 (when (and found auth-source-do-cache)
581 (auth-source-remember spec found))))
582
583 found))
552 584
553;;; (auth-source-search :max 1) 585;;; (auth-source-search :max 1)
554;;; (funcall (plist-get (nth 0 (auth-source-search :max 1)) :secret)) 586;;; (funcall (plist-get (nth 0 (auth-source-search :max 1)) :secret))
@@ -588,8 +620,9 @@ Returns the deleted entries."
588 620
589(defun auth-source-remember (spec found) 621(defun auth-source-remember (spec found)
590 "Remember FOUND search results for SPEC." 622 "Remember FOUND search results for SPEC."
591 (password-cache-add 623 (let ((password-cache-expiry auth-source-cache-expiry))
592 (concat auth-source-magic (format "%S" spec)) found)) 624 (password-cache-add
625 (concat auth-source-magic (format "%S" spec)) found)))
593 626
594(defun auth-source-recall (spec) 627(defun auth-source-recall (spec)
595 "Recall FOUND search results for SPEC." 628 "Recall FOUND search results for SPEC."
@@ -648,7 +681,7 @@ while \(:host t) would find all host entries."
648;;; (auth-source-netrc-parse "~/.authinfo.gpg") 681;;; (auth-source-netrc-parse "~/.authinfo.gpg")
649(defun* auth-source-netrc-parse (&rest 682(defun* auth-source-netrc-parse (&rest
650 spec 683 spec
651 &key file max host user protocol delete 684 &key file max host user port delete
652 &allow-other-keys) 685 &allow-other-keys)
653 "Parse FILE and return a list of all entries in the file. 686 "Parse FILE and return a list of all entries in the file.
654Note that the MAX parameter is used so we can exit the parse early." 687Note that the MAX parameter is used so we can exit the parse early."
@@ -710,18 +743,21 @@ Note that the MAX parameter is used so we can exit the parse early."
710 host 743 host
711 (or 744 (or
712 (aget alist "machine") 745 (aget alist "machine")
713 (aget alist "host"))) 746 (aget alist "host")
747 t))
714 (auth-source-search-collection 748 (auth-source-search-collection
715 user 749 user
716 (or 750 (or
717 (aget alist "login") 751 (aget alist "login")
718 (aget alist "account") 752 (aget alist "account")
719 (aget alist "user"))) 753 (aget alist "user")
754 t))
720 (auth-source-search-collection 755 (auth-source-search-collection
721 protocol 756 port
722 (or 757 (or
723 (aget alist "port") 758 (aget alist "port")
724 (aget alist "protocol")))) 759 (aget alist "protocol")
760 t)))
725 (decf max) 761 (decf max)
726 (push (nreverse alist) result) 762 (push (nreverse alist) result)
727 ;; to delete a line, we just comment it out 763 ;; to delete a line, we just comment it out
@@ -787,7 +823,7 @@ Note that the MAX parameter is used so we can exit the parse early."
787(defun* auth-source-netrc-search (&rest 823(defun* auth-source-netrc-search (&rest
788 spec 824 spec
789 &key backend create delete 825 &key backend create delete
790 type max host user protocol 826 type max host user port
791 &allow-other-keys) 827 &allow-other-keys)
792"Given a property list SPEC, return search matches from the :backend. 828"Given a property list SPEC, return search matches from the :backend.
793See `auth-source-search' for details on SPEC." 829See `auth-source-search' for details on SPEC."
@@ -802,20 +838,23 @@ See `auth-source-search' for details on SPEC."
802 :file (oref backend source) 838 :file (oref backend source)
803 :host (or host t) 839 :host (or host t)
804 :user (or user t) 840 :user (or user t)
805 :protocol (or protocol t))))) 841 :port (or port t)))))
806 842
807 ;; if we need to create an entry AND none were found to match 843 ;; if we need to create an entry AND none were found to match
808 (when (and create 844 (when (and create
809 (= 0 (length results))) 845 (= 0 (length results)))
810 846
811 ;; create based on the spec 847 ;; create based on the spec and record the value
812 (apply (slot-value backend 'create-function) spec) 848 (setq results (or
813 ;; turn off the :create key 849 ;; if the user did not want to create the entry
814 (setq spec (plist-put spec :create nil)) 850 ;; in the file, it will be returned
815 ;; run the search again to get the updated data 851 (apply (slot-value backend 'create-function) spec)
816 ;; the result will be returned, even if the search fails 852 ;; if not, we do the search again without :create
817 (setq results (apply 'auth-source-netrc-search spec))) 853 ;; to get the updated data.
818 854
855 ;; the result will be returned, even if the search fails
856 (apply 'auth-source-netrc-search
857 (plist-put spec :create nil)))))
819 results)) 858 results))
820 859
821;;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t) 860;;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t)
@@ -823,26 +862,33 @@ See `auth-source-search' for details on SPEC."
823 862
824(defun* auth-source-netrc-create (&rest spec 863(defun* auth-source-netrc-create (&rest spec
825 &key backend 864 &key backend
826 secret host user protocol create 865 secret host user port create
827 &allow-other-keys) 866 &allow-other-keys)
828 (let* ((base-required '(host user protocol secret)) 867 (let* ((base-required '(host user port secret))
829 ;; we know (because of an assertion in auth-source-search) that the 868 ;; we know (because of an assertion in auth-source-search) that the
830 ;; :create parameter is either t or a list (which includes nil) 869 ;; :create parameter is either t or a list (which includes nil)
831 (create-extra (if (eq t create) nil create)) 870 (create-extra (if (eq t create) nil create))
832 (required (append base-required create-extra)) 871 (required (append base-required create-extra))
833 (file (oref backend source)) 872 (file (oref backend source))
834 (add "") 873 (add "")
874 (show "")
835 ;; `valist' is an alist 875 ;; `valist' is an alist
836 valist) 876 valist
877 ;; `artificial' will be returned if no creation is needed
878 artificial)
837 879
838 ;; only for base required elements (defined as function parameters): 880 ;; only for base required elements (defined as function parameters):
839 ;; fill in the valist with whatever data we may have from the search 881 ;; fill in the valist with whatever data we may have from the search
840 ;; we take the first value if it's a list, the whole value otherwise 882 ;; we complete the first value if it's a list and use the value otherwise
841 (dolist (br base-required) 883 (dolist (br base-required)
842 (when (symbol-value br) 884 (when (symbol-value br)
843 (aput 'valist br (if (listp (symbol-value br)) 885 (let ((br-choice (cond
844 (nth 0 (symbol-value br)) 886 ;; all-accepting choice (predicate is t)
845 (symbol-value br))))) 887 ((eq t (symbol-value br)) nil)
888 ;; just the value otherwise
889 (t (symbol-value br)))))
890 (when br-choice
891 (aput 'valist br br-choice)))))
846 892
847 ;; for extra required elements, see if the spec includes a value for them 893 ;; for extra required elements, see if the spec includes a value for them
848 (dolist (er create-extra) 894 (dolist (er create-extra)
@@ -862,7 +908,7 @@ See `auth-source-search' for details on SPEC."
862 ((and (not given-default) (eq r 'user)) 908 ((and (not given-default) (eq r 'user))
863 (user-login-name)) 909 (user-login-name))
864 ;; note we need this empty string 910 ;; note we need this empty string
865 ((and (not given-default) (eq r 'protocol)) 911 ((and (not given-default) (eq r 'port))
866 "") 912 "")
867 (t given-default))) 913 (t given-default)))
868 ;; the prompt's default string depends on the data so far 914 ;; the prompt's default string depends on the data so far
@@ -872,20 +918,22 @@ See `auth-source-search' for details on SPEC."
872 ;; the prompt should also show what's entered so far 918 ;; the prompt should also show what's entered so far
873 (user-value (aget valist 'user)) 919 (user-value (aget valist 'user))
874 (host-value (aget valist 'host)) 920 (host-value (aget valist 'host))
875 (protocol-value (aget valist 'protocol)) 921 (port-value (aget valist 'port))
922 ;; note this handles lists by just printing them
923 ;; later we allow the user to use completing-read to pick
876 (info-so-far (concat (if user-value 924 (info-so-far (concat (if user-value
877 (format "%s@" user-value) 925 (format "%s@" user-value)
878 "[USER?]") 926 "[USER?]")
879 (if host-value 927 (if host-value
880 (format "%s" host-value) 928 (format "%s" host-value)
881 "[HOST?]") 929 "[HOST?]")
882 (if protocol-value 930 (if port-value
883 ;; this distinguishes protocol between 931 ;; this distinguishes protocol between
884 (if (zerop (length protocol-value)) 932 (if (zerop (length port-value))
885 "" ; 'entered as "no default"' vs. 933 "" ; 'entered as "no default"' vs.
886 (format ":%s" protocol-value)) ; given 934 (format ":%s" port-value)) ; given
887 ;; and this is when the protocol is unknown 935 ;; and this is when the protocol is unknown
888 "[PROTOCOL?]")))) 936 "[PORT?]"))))
889 937
890 ;; now prompt if the search SPEC did not include a required key; 938 ;; now prompt if the search SPEC did not include a required key;
891 ;; take the result and put it in `data' AND store it in `valist' 939 ;; take the result and put it in `data' AND store it in `valist'
@@ -900,25 +948,48 @@ See `auth-source-search' for details on SPEC."
900 (format "Enter %s for %s%s: " 948 (format "Enter %s for %s%s: "
901 r info-so-far default-string) 949 r info-so-far default-string)
902 nil nil default)) 950 nil nil default))
951 ((listp data)
952 (completing-read
953 (format "Enter %s for %s (TAB to see the choices): "
954 r info-so-far)
955 data
956 nil ; no predicate
957 t ; require a match
958 ;; note the default is nil, but if the user
959 ;; hits RET we'll get "", which is handled OK later
960 nil))
903 (t data)))) 961 (t data))))
904 962
963 (when data
964 (setq artificial (plist-put artificial
965 (intern (concat ":" (symbol-name r)))
966 (if (eq r 'secret)
967 (lexical-let ((data data))
968 (lambda () data))
969 data))))
970
905 ;; when r is not an empty string... 971 ;; when r is not an empty string...
906 (when (and (stringp data) 972 (when (and (stringp data)
907 (< 0 (length data))) 973 (< 0 (length data)))
908 ;; append the key (the symbol name of r) and the value in r 974 (let ((printer (lambda (hide)
909 (setq add (concat add 975 ;; append the key (the symbol name of r)
910 (format "%s%s %S" 976 ;; and the value in r
911 ;; prepend a space 977 (format "%s%s %S"
912 (if (zerop (length add)) "" " ") 978 ;; prepend a space
913 ;; remap auth-source tokens to netrc 979 (if (zerop (length add)) "" " ")
914 (case r 980 ;; remap auth-source tokens to netrc
981 (case r
915 ('user "login") 982 ('user "login")
916 ('host "machine") 983 ('host "machine")
917 ('secret "password") 984 ('secret "password")
918 ('protocol "port") 985 ('port "port") ; redundant but clearer
919 (t (symbol-name r))) 986 (t (symbol-name r)))
920 ;; the value will be printed in %S format 987 ;; the value will be printed in %S format
921 data)))))) 988 (if (and hide (eq r 'secret))
989 "HIDDEN_SECRET"
990 data)))))
991 (setq add (concat add (funcall printer nil)))
992 (setq show (concat show (funcall printer t)))))))
922 993
923 (with-temp-buffer 994 (with-temp-buffer
924 (when (file-exists-p file) 995 (when (file-exists-p file)
@@ -935,14 +1006,17 @@ See `auth-source-search' for details on SPEC."
935 (goto-char (point-max)) 1006 (goto-char (point-max))
936 1007
937 ;; ask AFTER we've successfully opened the file 1008 ;; ask AFTER we've successfully opened the file
938 (when (y-or-n-p (format "Add to file %s: line [%s]" file add)) 1009 (if (y-or-n-p (format "Add to file %s: line [%s]" file show))
939 (unless (bolp) 1010 (progn
940 (insert "\n")) 1011 (unless (bolp)
941 (insert add "\n") 1012 (insert "\n"))
942 (write-region (point-min) (point-max) file nil 'silent) 1013 (insert add "\n")
943 (auth-source-do-debug 1014 (write-region (point-min) (point-max) file nil 'silent)
944 "auth-source-netrc-create: wrote 1 new line to %s" 1015 (auth-source-do-debug
945 file))))) 1016 "auth-source-netrc-create: wrote 1 new line to %s"
1017 file)
1018 nil)
1019 (list artificial)))))
946 1020
947;;; Backend specific parsing: Secrets API backend 1021;;; Backend specific parsing: Secrets API backend
948 1022
@@ -956,7 +1030,7 @@ See `auth-source-search' for details on SPEC."
956(defun* auth-source-secrets-search (&rest 1030(defun* auth-source-secrets-search (&rest
957 spec 1031 spec
958 &key backend create delete label 1032 &key backend create delete label
959 type max host user protocol 1033 type max host user port
960 &allow-other-keys) 1034 &allow-other-keys)
961 "Search the Secrets API; spec is like `auth-source'. 1035 "Search the Secrets API; spec is like `auth-source'.
962 1036
@@ -1012,10 +1086,10 @@ authentication tokens:
1012 nil 1086 nil
1013 (list k (plist-get spec k)))) 1087 (list k (plist-get spec k))))
1014 search-keys))) 1088 search-keys)))
1015 ;; needed keys (always including host, login, protocol, and secret) 1089 ;; needed keys (always including host, login, port, and secret)
1016 (returned-keys (delete-dups (append 1090 (returned-keys (mm-delete-duplicates (append
1017 '(:host :login :protocol :secret) 1091 '(:host :login :port :secret)
1018 search-keys))) 1092 search-keys)))
1019 (items (loop for item in (apply 'secrets-search-items coll search-spec) 1093 (items (loop for item in (apply 'secrets-search-items coll search-spec)
1020 unless (and (stringp label) 1094 unless (and (stringp label)
1021 (not (string-match label item))) 1095 (not (string-match label item)))
@@ -1051,7 +1125,7 @@ authentication tokens:
1051 1125
1052(defun* auth-source-secrets-create (&rest 1126(defun* auth-source-secrets-create (&rest
1053 spec 1127 spec
1054 &key backend type max host user protocol 1128 &key backend type max host user port
1055 &allow-other-keys) 1129 &allow-other-keys)
1056 ;; TODO 1130 ;; TODO
1057 ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec) 1131 ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec)
@@ -1068,8 +1142,8 @@ authentication tokens:
1068 'auth-source-forget "Emacs 24.1") 1142 'auth-source-forget "Emacs 24.1")
1069 1143
1070(defun auth-source-user-or-password 1144(defun auth-source-user-or-password
1071 (mode host protocol &optional username create-missing delete-existing) 1145 (mode host port &optional username create-missing delete-existing)
1072 "Find MODE (string or list of strings) matching HOST and PROTOCOL. 1146 "Find MODE (string or list of strings) matching HOST and PORT.
1073 1147
1074DEPRECATED in favor of `auth-source-search'! 1148DEPRECATED in favor of `auth-source-search'!
1075 1149
@@ -1092,14 +1166,14 @@ stored in the password database which matches best (see
1092MODE can be \"login\" or \"password\"." 1166MODE can be \"login\" or \"password\"."
1093 (auth-source-do-debug 1167 (auth-source-do-debug
1094 "auth-source-user-or-password: DEPRECATED get %s for %s (%s) + user=%s" 1168 "auth-source-user-or-password: DEPRECATED get %s for %s (%s) + user=%s"
1095 mode host protocol username) 1169 mode host port username)
1096 1170
1097 (let* ((listy (listp mode)) 1171 (let* ((listy (listp mode))
1098 (mode (if listy mode (list mode))) 1172 (mode (if listy mode (list mode)))
1099 (cname (if username 1173 (cname (if username
1100 (format "%s %s:%s %s" mode host protocol username) 1174 (format "%s %s:%s %s" mode host port username)
1101 (format "%s %s:%s" mode host protocol))) 1175 (format "%s %s:%s" mode host port)))
1102 (search (list :host host :protocol protocol)) 1176 (search (list :host host :port port))
1103 (search (if username (append search (list :user username)) search)) 1177 (search (if username (append search (list :user username)) search))
1104 (search (if create-missing 1178 (search (if create-missing
1105 (append search (list :create t)) 1179 (append search (list :create t))
@@ -1121,7 +1195,7 @@ MODE can be \"login\" or \"password\"."
1121 (if (and (member "password" mode) t) 1195 (if (and (member "password" mode) t)
1122 "SECRET" 1196 "SECRET"
1123 found) 1197 found)
1124 host protocol username) 1198 host port username)
1125 found) ; return the found data 1199 found) ; return the found data
1126 ;; else, if not found, search with a max of 1 1200 ;; else, if not found, search with a max of 1
1127 (let ((choice (nth 0 (apply 'auth-source-search 1201 (let ((choice (nth 0 (apply 'auth-source-search
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index 4dfc79a8883..619c8bd75fd 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -1234,11 +1234,10 @@ For example: ((1 . cn-gb-2312) (2 . big5))."
1234 :type 'boolean 1234 :type 'boolean
1235 :group 'gnus-summary-marks) 1235 :group 'gnus-summary-marks)
1236 1236
1237(defcustom gnus-propagate-marks nil 1237(defcustom gnus-propagate-marks t
1238 "If non-nil, Gnus will store and retrieve marks from the backends. 1238 "If non-nil, Gnus will store and retrieve marks from the backends.
1239This means that marks will be stored both in .newsrc.eld and in 1239This means that marks will be stored both in .newsrc.eld and in
1240the backend, and will slow operation down somewhat." 1240the backend, and will slow operation down somewhat."
1241 :version "24.1"
1242 :type 'boolean 1241 :type 'boolean
1243 :group 'gnus-summary-marks) 1242 :group 'gnus-summary-marks)
1244 1243
diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el
index 67c49096b92..42dbd5948cf 100644
--- a/lisp/gnus/gnus-util.el
+++ b/lisp/gnus/gnus-util.el
@@ -871,6 +871,15 @@ Bind `print-quoted' and `print-readably' to t, and `print-length' and
871 (when (file-exists-p file) 871 (when (file-exists-p file)
872 (delete-file file))) 872 (delete-file file)))
873 873
874(defun gnus-delete-duplicates (list)
875 "Remove duplicate entries from LIST."
876 (let ((result nil))
877 (while list
878 (unless (member (car list) result)
879 (push (car list) result))
880 (pop list))
881 (nreverse result)))
882
874(defun gnus-delete-directory (directory) 883(defun gnus-delete-directory (directory)
875 "Delete files in DIRECTORY. Subdirectories remain. 884 "Delete files in DIRECTORY. Subdirectories remain.
876If there's no subdirectory, delete DIRECTORY as well." 885If there's no subdirectory, delete DIRECTORY as well."
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index a5a001f7e11..9c93ee8bbd9 100644
--- a/lisp/gnus/nnimap.el
+++ b/lisp/gnus/nnimap.el
@@ -276,18 +276,17 @@ textual parts.")
276 (push (current-buffer) nnimap-process-buffers) 276 (push (current-buffer) nnimap-process-buffers)
277 (current-buffer))) 277 (current-buffer)))
278 278
279(defun nnimap-credentials (address ports &optional inhibit-create) 279(defun nnimap-credentials (address ports)
280 (let* ((found (nth 0 (auth-source-search :max 1 280 (let ((found (nth 0 (auth-source-search :max 1
281 :host address 281 :host address
282 :port ports 282 :port ports
283 :create (if inhibit-create 283 :create t))))
284 nil
285 (null ports)))))
286 (user (plist-get found :user))
287 (secret (plist-get found :secret))
288 (secret (if (functionp secret) (funcall secret) secret)))
289 (if found 284 (if found
290 (list user secret) 285 (list (plist-get found :user)
286 (let ((secret (plist-get found :secret)))
287 (if (functionp secret)
288 (funcall secret)
289 secret)))
291 nil))) 290 nil)))
292 291
293(defun nnimap-keepalive () 292(defun nnimap-keepalive ()
@@ -386,10 +385,11 @@ textual parts.")
386 ;; Look for the credentials based on 385 ;; Look for the credentials based on
387 ;; the virtual server name and the address 386 ;; the virtual server name and the address
388 (nnimap-credentials 387 (nnimap-credentials
389 (list 388 (gnus-delete-duplicates
390 (nnoo-current-server 'nnimap) 389 (list
391 nnimap-address) 390 nnimap-address
392 ports t)))) 391 (nnoo-current-server 'nnimap)))
392 ports))))
393 (setq nnimap-object nil) 393 (setq nnimap-object nil)
394 (let ((nnimap-inhibit-logging t)) 394 (let ((nnimap-inhibit-logging t))
395 (setq login-result 395 (setq login-result
@@ -400,7 +400,7 @@ textual parts.")
400 (dolist (host (list (nnoo-current-server 'nnimap) 400 (dolist (host (list (nnoo-current-server 'nnimap)
401 nnimap-address)) 401 nnimap-address))
402 (dolist (port ports) 402 (dolist (port ports)
403 (auth-source-forget+ :host host :protocol port))) 403 (auth-source-forget+ :host host :port port)))
404 (delete-process (nnimap-process nnimap-object)) 404 (delete-process (nnimap-process nnimap-object))
405 (setq nnimap-object nil)))) 405 (setq nnimap-object nil))))
406 (when nnimap-object 406 (when nnimap-object
@@ -1075,60 +1075,62 @@ textual parts.")
1075 (nreverse groups))) 1075 (nreverse groups)))
1076 1076
1077(deffoo nnimap-request-list (&optional server) 1077(deffoo nnimap-request-list (&optional server)
1078 (nnimap-possibly-change-group nil server) 1078 (when (nnimap-possibly-change-group nil server)
1079 (with-current-buffer nntp-server-buffer 1079 (with-current-buffer nntp-server-buffer
1080 (erase-buffer) 1080 (erase-buffer)
1081 (let ((groups 1081 (let ((groups
1082 (with-current-buffer (nnimap-buffer) 1082 (with-current-buffer (nnimap-buffer)
1083 (nnimap-get-groups))) 1083 (nnimap-get-groups)))
1084 sequences responses) 1084 sequences responses)
1085 (when groups 1085 (when groups
1086 (with-current-buffer (nnimap-buffer) 1086 (with-current-buffer (nnimap-buffer)
1087 (setf (nnimap-group nnimap-object) nil) 1087 (setf (nnimap-group nnimap-object) nil)
1088 (dolist (group groups) 1088 (dolist (group groups)
1089 (setf (nnimap-examined nnimap-object) group) 1089 (setf (nnimap-examined nnimap-object) group)
1090 (push (list (nnimap-send-command "EXAMINE %S" (utf7-encode group t)) 1090 (push (list (nnimap-send-command "EXAMINE %S"
1091 group) 1091 (utf7-encode group t))
1092 sequences)) 1092 group)
1093 (nnimap-wait-for-response (caar sequences)) 1093 sequences))
1094 (setq responses 1094 (nnimap-wait-for-response (caar sequences))
1095 (nnimap-get-responses (mapcar #'car sequences)))) 1095 (setq responses
1096 (dolist (response responses) 1096 (nnimap-get-responses (mapcar #'car sequences))))
1097 (let* ((sequence (car response)) 1097 (dolist (response responses)
1098 (response (cadr response)) 1098 (let* ((sequence (car response))
1099 (group (cadr (assoc sequence sequences)))) 1099 (response (cadr response))
1100 (when (and group 1100 (group (cadr (assoc sequence sequences))))
1101 (equal (caar response) "OK")) 1101 (when (and group
1102 (let ((uidnext (nnimap-find-parameter "UIDNEXT" response)) 1102 (equal (caar response) "OK"))
1103 highest exists) 1103 (let ((uidnext (nnimap-find-parameter "UIDNEXT" response))
1104 (dolist (elem response) 1104 highest exists)
1105 (when (equal (cadr elem) "EXISTS") 1105 (dolist (elem response)
1106 (setq exists (string-to-number (car elem))))) 1106 (when (equal (cadr elem) "EXISTS")
1107 (when uidnext 1107 (setq exists (string-to-number (car elem)))))
1108 (setq highest (1- (string-to-number (car uidnext))))) 1108 (when uidnext
1109 (cond 1109 (setq highest (1- (string-to-number (car uidnext)))))
1110 ((null highest) 1110 (cond
1111 (insert (format "%S 0 1 y\n" (utf7-decode group t)))) 1111 ((null highest)
1112 ((zerop exists) 1112 (insert (format "%S 0 1 y\n" (utf7-decode group t))))
1113 ;; Empty group. 1113 ((zerop exists)
1114 (insert (format "%S %d %d y\n" 1114 ;; Empty group.
1115 (utf7-decode group t) highest (1+ highest)))) 1115 (insert (format "%S %d %d y\n"
1116 (t 1116 (utf7-decode group t)
1117 ;; Return the widest possible range. 1117 highest (1+ highest))))
1118 (insert (format "%S %d 1 y\n" (utf7-decode group t) 1118 (t
1119 (or highest exists))))))))) 1119 ;; Return the widest possible range.
1120 t)))) 1120 (insert (format "%S %d 1 y\n" (utf7-decode group t)
1121 (or highest exists)))))))))
1122 t)))))
1121 1123
1122(deffoo nnimap-request-newgroups (date &optional server) 1124(deffoo nnimap-request-newgroups (date &optional server)
1123 (nnimap-possibly-change-group nil server) 1125 (when (nnimap-possibly-change-group nil server)
1124 (with-current-buffer nntp-server-buffer 1126 (with-current-buffer nntp-server-buffer
1125 (erase-buffer) 1127 (erase-buffer)
1126 (dolist (group (with-current-buffer (nnimap-buffer) 1128 (dolist (group (with-current-buffer (nnimap-buffer)
1127 (nnimap-get-groups))) 1129 (nnimap-get-groups)))
1128 (unless (assoc group nnimap-current-infos) 1130 (unless (assoc group nnimap-current-infos)
1129 ;; Insert dummy numbers here -- they don't matter. 1131 ;; Insert dummy numbers here -- they don't matter.
1130 (insert (format "%S 0 1 y\n" group)))) 1132 (insert (format "%S 0 1 y\n" group))))
1131 t)) 1133 t)))
1132 1134
1133(deffoo nnimap-retrieve-group-data-early (server infos) 1135(deffoo nnimap-retrieve-group-data-early (server infos)
1134 (when (nnimap-possibly-change-group nil server) 1136 (when (nnimap-possibly-change-group nil server)
@@ -1589,7 +1591,7 @@ textual parts.")
1589 (goto-char (point-max)) 1591 (goto-char (point-max))
1590 (insert (format-time-string "%H:%M:%S") " " 1592 (insert (format-time-string "%H:%M:%S") " "
1591 (if nnimap-inhibit-logging 1593 (if nnimap-inhibit-logging
1592 "(inhibited)" 1594 "(inhibited)\n"
1593 command))) 1595 command)))
1594 command) 1596 command)
1595 1597
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index 8657dc58bf4..1d419dbfa18 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -556,6 +556,11 @@ If ARG is non-nil, instead prompt for connection parameters."
556 `(with-current-buffer rcirc-server-buffer 556 `(with-current-buffer rcirc-server-buffer
557 ,@body)) 557 ,@body))
558 558
559(defun rcirc-float-time ()
560 (if (featurep 'xemacs)
561 (time-to-seconds (current-time))
562 (float-time)))
563
559(defun rcirc-keepalive () 564(defun rcirc-keepalive ()
560 "Send keep alive pings to active rcirc processes. 565 "Send keep alive pings to active rcirc processes.
561Kill processes that have not received a server message since the 566Kill processes that have not received a server message since the
@@ -567,10 +572,7 @@ last ping."
567 (rcirc-send-ctcp process 572 (rcirc-send-ctcp process
568 rcirc-nick 573 rcirc-nick
569 (format "KEEPALIVE %f" 574 (format "KEEPALIVE %f"
570 (if (featurep 'xemacs) 575 (rcirc-float-time))))))
571 (time-to-seconds
572 (current-time))
573 (float-time)))))))
574 (rcirc-process-list)) 576 (rcirc-process-list))
575 ;; no processes, clean up timer 577 ;; no processes, clean up timer
576 (cancel-timer rcirc-keepalive-timer) 578 (cancel-timer rcirc-keepalive-timer)
@@ -578,10 +580,7 @@ last ping."
578 580
579(defun rcirc-handler-ctcp-KEEPALIVE (process target sender message) 581(defun rcirc-handler-ctcp-KEEPALIVE (process target sender message)
580 (with-rcirc-process-buffer process 582 (with-rcirc-process-buffer process
581 (setq header-line-format (format "%f" (- (if (featurep 'xemacs) 583 (setq header-line-format (format "%f" (- (rcirc-float-time)
582 (time-to-seconds
583 (current-time))
584 (float-time))
585 (string-to-number message)))))) 584 (string-to-number message))))))
586 585
587(defvar rcirc-debug-buffer " *rcirc debug*") 586(defvar rcirc-debug-buffer " *rcirc debug*")
@@ -2209,7 +2208,7 @@ With a prefix arg, prompt for new topic."
2209 2208
2210(defun rcirc-ctcp-sender-PING (process target request) 2209(defun rcirc-ctcp-sender-PING (process target request)
2211 "Send a CTCP PING message to TARGET." 2210 "Send a CTCP PING message to TARGET."
2212 (let ((timestamp (format "%.0f" (float-time)))) 2211 (let ((timestamp (format "%.0f" (rcirc-float-time))))
2213 (rcirc-send-ctcp process target "PING" timestamp))) 2212 (rcirc-send-ctcp process target "PING" timestamp)))
2214 2213
2215(defun rcirc-cmd-me (args &optional process target) 2214(defun rcirc-cmd-me (args &optional process target)
diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el
new file mode 100644
index 00000000000..b4307223ba8
--- /dev/null
+++ b/lisp/net/soap-client.el
@@ -0,0 +1,1741 @@
1;;;; soap-client.el -- Access SOAP web services from Emacs
2
3;; Copyright (C) 2009-2011 Free Software Foundation, Inc.
4
5;; Author: Alexandru Harsanyi (AlexHarsanyi@gmail.com)
6;; Created: December, 2009
7;; Keywords: soap, web-services, comm, hypermedia
8;; Homepage: http://code.google.com/p/emacs-soap-client
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25;;; Commentary:
26;;
27;; To use the SOAP client, you first need to load the WSDL document for the
28;; service you want to access, using `soap-load-wsdl-from-url'. A WSDL
29;; document describes the available operations of the SOAP service, how their
30;; parameters and responses are encoded. To invoke operations, you use the
31;; `soap-invoke' method passing it the WSDL, the service name, the operation
32;; you wish to invoke and any required parameters.
33;;
34;; Idealy, the service you want to access will have some documentation about
35;; the operations it supports. If it does not, you can try using
36;; `soap-inspect' to browse the WSDL document and see the available operations
37;; and their parameters.
38;;
39
40;;; Code:
41
42(eval-when-compile (require 'cl))
43
44(require 'xml)
45(require 'warnings)
46(require 'url)
47(require 'url-http)
48(require 'url-util)
49(require 'mm-decode)
50
51(defsubst soap-warning (message &rest args)
52 "Display a warning MESSAGE with ARGS, using the 'soap-client warning type."
53 (display-warning 'soap-client (apply 'format message args) :warning))
54
55(defgroup soap-client nil
56 "Access SOAP web services from Emacs."
57 :group 'tools)
58
59;;;; Support for parsing XML documents with namespaces
60
61;; XML documents with namespaces are difficult to parse because the names of
62;; the nodes depend on what "xmlns" aliases have been defined in the document.
63;; To work with such documents, we introduce a translation layer between a
64;; "well known" namespace tag and the local namespace tag in the document
65;; being parsed.
66
67(defconst soap-well-known-xmlns
68 '(("apachesoap" . "http://xml.apache.org/xml-soap")
69 ("soapenc" . "http://schemas.xmlsoap.org/soap/encoding/")
70 ("wsdl" . "http://schemas.xmlsoap.org/wsdl/")
71 ("wsdlsoap" . "http://schemas.xmlsoap.org/wsdl/soap/")
72 ("xsd" . "http://www.w3.org/2001/XMLSchema")
73 ("xsi" . "http://www.w3.org/2001/XMLSchema-instance")
74 ("soap" . "http://schemas.xmlsoap.org/soap/envelope/")
75 ("soap12" . "http://schemas.xmlsoap.org/wsdl/soap12/")
76 ("http" . "http://schemas.xmlsoap.org/wsdl/http/")
77 ("mime" . "http://schemas.xmlsoap.org/wsdl/mime/"))
78 "A list of well known xml namespaces and their aliases.")
79
80(defvar soap-local-xmlns nil
81 "A list of local namespace aliases.
82This is a dynamically bound variable, controlled by
83`soap-with-local-xmlns'.")
84
85(defvar soap-default-xmlns nil
86 "The default XML namespaces.
87Names in this namespace will be unqualified. This is a
88dynamically bound variable, controlled by
89`soap-with-local-xmlns'")
90
91(defvar soap-target-xmlns nil
92 "The target XML namespace.
93New XSD elements will be defined in this namespace, unless they
94are fully qualified for a different namespace. This is a
95dynamically bound variable, controlled by
96`soap-with-local-xmlns'")
97
98(defun soap-wk2l (well-known-name)
99 "Return local variant of WELL-KNOWN-NAME.
100This is done by looking up the namespace in the
101`soap-well-known-xmlns' table and resolving the namespace to
102the local name based on the current local translation table
103`soap-local-xmlns'. See also `soap-with-local-xmlns'."
104 (let ((wk-name-1 (if (symbolp well-known-name)
105 (symbol-name well-known-name)
106 well-known-name)))
107 (cond
108 ((string-match "^\\(.*\\):\\(.*\\)$" wk-name-1)
109 (let ((ns (match-string 1 wk-name-1))
110 (name (match-string 2 wk-name-1)))
111 (let ((namespace (cdr (assoc ns soap-well-known-xmlns))))
112 (cond ((equal namespace soap-default-xmlns)
113 ;; Name is unqualified in the default namespace
114 (if (symbolp well-known-name)
115 (intern name)
116 name))
117 (t
118 (let* ((local-ns (car (rassoc namespace soap-local-xmlns)))
119 (local-name (concat local-ns ":" name)))
120 (if (symbolp well-known-name)
121 (intern local-name)
122 local-name)))))))
123 (t well-known-name))))
124
125(defun soap-l2wk (local-name)
126 "Convert LOCAL-NAME into a well known name.
127The namespace of LOCAL-NAME is looked up in the
128`soap-well-known-xmlns' table and a well known namespace tag is
129used in the name.
130
131nil is returned if there is no well-known namespace for the
132namespace of LOCAL-NAME."
133 (let ((l-name-1 (if (symbolp local-name)
134 (symbol-name local-name)
135 local-name))
136 namespace name)
137 (cond
138 ((string-match "^\\(.*\\):\\(.*\\)$" l-name-1)
139 (setq name (match-string 2 l-name-1))
140 (let ((ns (match-string 1 l-name-1)))
141 (setq namespace (cdr (assoc ns soap-local-xmlns)))
142 (unless namespace
143 (error "Soap-l2wk(%s): no namespace for alias %s" local-name ns))))
144 (t
145 (setq name l-name-1)
146 (setq namespace soap-default-xmlns)))
147
148 (if namespace
149 (let ((well-known-ns (car (rassoc namespace soap-well-known-xmlns))))
150 (if well-known-ns
151 (let ((well-known-name (concat well-known-ns ":" name)))
152 (if (symbol-name local-name)
153 (intern well-known-name)
154 well-known-name))
155 (progn
156 ;; (soap-warning "soap-l2wk(%s): namespace %s has no well-known tag"
157 ;; local-name namespace)
158 nil)))
159 ;; if no namespace is defined, just return the unqualified name
160 name)))
161
162
163(defun soap-l2fq (local-name &optional use-tns)
164 "Convert LOCAL-NAME into a fully qualified name.
165A fully qualified name is a cons of the namespace name and the
166name of the element itself. For example \"xsd:string\" is
167converted to \(\"http://www.w3.org/2001/XMLSchema\" . \"string\"\).
168
169The USE-TNS argument specifies what to do when LOCAL-NAME has no
170namespace tag. If USE-TNS is non-nil, the `soap-target-xmlns'
171will be used as the element's namespace, otherwise
172`soap-default-xmlns' will be used.
173
174This is needed because different parts of a WSDL document can use
175different namespace aliases for the same element."
176 (let ((local-name-1 (if (symbolp local-name)
177 (symbol-name local-name)
178 local-name)))
179 (cond ((string-match "^\\(.*\\):\\(.*\\)$" local-name-1)
180 (let ((ns (match-string 1 local-name-1))
181 (name (match-string 2 local-name-1)))
182 (let ((namespace (cdr (assoc ns soap-local-xmlns))))
183 (if namespace
184 (cons namespace name)
185 (error "Soap-l2fq(%s): unknown alias %s" local-name ns)))))
186 (t
187 (cons (if use-tns
188 soap-target-xmlns
189 soap-default-xmlns)
190 local-name)))))
191
192(defun soap-extract-xmlns (node &optional xmlns-table)
193 "Return a namespace alias table for NODE by extending XMLNS-TABLE."
194 (let (xmlns default-ns target-ns)
195 (dolist (a (xml-node-attributes node))
196 (let ((name (symbol-name (car a)))
197 (value (cdr a)))
198 (cond ((string= name "targetNamespace")
199 (setq target-ns value))
200 ((string= name "xmlns")
201 (setq default-ns value))
202 ((string-match "^xmlns:\\(.*\\)$" name)
203 (push (cons (match-string 1 name) value) xmlns)))))
204
205 (let ((tns (assoc "tns" xmlns)))
206 (cond ((and tns target-ns)
207 ;; If a tns alias is defined for this node, it must match
208 ;; the target namespace.
209 (unless (equal target-ns (cdr tns))
210 (soap-warning
211 "soap-extract-xmlns(%s): tns alias and targetNamespace mismatch"
212 (xml-node-name node))))
213 ((and tns (not target-ns))
214 (setq target-ns (cdr tns)))
215 ((and (not tns) target-ns)
216 ;; a tns alias was not defined in this node. See if the node has
217 ;; a "targetNamespace" attribute and add an alias to this. Note
218 ;; that we might override an existing tns alias in XMLNS-TABLE,
219 ;; but that is intended.
220 (push (cons "tns" target-ns) xmlns))))
221
222 (list default-ns target-ns (append xmlns xmlns-table))))
223
224(defmacro soap-with-local-xmlns (node &rest body)
225 "Install a local alias table from NODE and execute BODY."
226 (declare (debug (form &rest form)) (indent 1))
227 (let ((xmlns (make-symbol "xmlns")))
228 `(let ((,xmlns (soap-extract-xmlns ,node soap-local-xmlns)))
229 (let ((soap-default-xmlns (or (nth 0 ,xmlns) soap-default-xmlns))
230 (soap-target-xmlns (or (nth 1 ,xmlns) soap-target-xmlns))
231 (soap-local-xmlns (nth 2 ,xmlns)))
232 ,@body))))
233
234(defun soap-get-target-namespace (node)
235 "Return the target namespace of NODE.
236This is the namespace in which new elements will be defined."
237 (or (xml-get-attribute-or-nil node 'targetNamespace)
238 (cdr (assoc "tns" soap-local-xmlns))
239 soap-target-xmlns))
240
241(defun soap-xml-get-children1 (node child-name)
242 "Return the children of NODE named CHILD-NAME.
243This is the same as `xml-get-children', but CHILD-NAME can have
244namespace tag."
245 (let (result)
246 (dolist (c (xml-node-children node))
247 (when (and (consp c)
248 (soap-with-local-xmlns c
249 ;; We use `ignore-errors' here because we want to silently
250 ;; skip nodes for which we cannot convert them to a
251 ;; well-known name.
252 (eq (ignore-errors (soap-l2wk (xml-node-name c)))
253 child-name)))
254 (push c result)))
255 (nreverse result)))
256
257(defun soap-xml-get-attribute-or-nil1 (node attribute)
258 "Return the NODE's ATTRIBUTE, or nil if it does not exist.
259This is the same as `xml-get-attribute-or-nil', but ATTRIBUTE can
260be tagged with a namespace tag."
261 (catch 'found
262 (soap-with-local-xmlns node
263 (dolist (a (xml-node-attributes node))
264 ;; We use `ignore-errors' here because we want to silently skip
265 ;; attributes for which we cannot convert them to a well-known name.
266 (when (eq (ignore-errors (soap-l2wk (car a))) attribute)
267 (throw 'found (cdr a)))))))
268
269
270;;;; XML namespaces
271
272;; An element in an XML namespace, "things" stored in soap-xml-namespaces will
273;; be derived from this object.
274
275(defstruct soap-element
276 name
277 ;; The "well-known" namespace tag for the element. For example, while
278 ;; parsing XML documents, we can have different tags for the XMLSchema
279 ;; namespace, but internally all our XMLSchema elements will have the "xsd"
280 ;; tag.
281 namespace-tag)
282
283(defun soap-element-fq-name (element)
284 "Return a fully qualified name for ELEMENT.
285A fq name is the concatenation of the namespace tag and the
286element name."
287 (concat (soap-element-namespace-tag element)
288 ":" (soap-element-name element)))
289
290;; a namespace link stores an alias for an object in once namespace to a
291;; "target" object possibly in a different namespace
292
293(defstruct (soap-namespace-link (:include soap-element))
294 target)
295
296;; A namespace is a collection of soap-element objects under a name (the name
297;; of the namespace).
298
299(defstruct soap-namespace
300 (name nil :read-only t) ; e.g "http://xml.apache.org/xml-soap"
301 (elements (make-hash-table :test 'equal) :read-only t))
302
303(defun soap-namespace-put (element ns)
304 "Store ELEMENT in NS.
305Multiple elements with the same name can be stored in a
306namespace. When retrieving the element you can specify a
307discriminant predicate to `soap-namespace-get'"
308 (let ((name (soap-element-name element)))
309 (push element (gethash name (soap-namespace-elements ns)))))
310
311(defun soap-namespace-put-link (name target ns &optional replace)
312 "Store a link from NAME to TARGET in NS.
313An error will be signaled if an element by the same name is
314already present in NS, unless REPLACE is non nil.
315
316TARGET can be either a SOAP-ELEMENT or a string denoting an
317element name into another namespace.
318
319If NAME is nil, an element with the same name as TARGET will be
320added to the namespace."
321
322 (unless (and name (not (equal name "")))
323 ;; if name is nil, use TARGET as a name...
324 (cond ((soap-element-p target)
325 (setq name (soap-element-name target)))
326 ((stringp target)
327 (cond ((string-match "^\\(.*\\):\\(.*\\)$" target)
328 (setq name (match-string 2 target)))
329 (t
330 (setq name target))))))
331
332 (assert name) ; by now, name should be valid
333 (push (make-soap-namespace-link :name name :target target)
334 (gethash name (soap-namespace-elements ns))))
335
336(defun soap-namespace-get (name ns &optional discriminant-predicate)
337 "Retrieve an element with NAME from the namespace NS.
338If multiple elements with the same name exist,
339DISCRIMINANT-PREDICATE is used to pick one of them. This allows
340storing elements of different types (like a message type and a
341binding) but the same name."
342 (assert (stringp name))
343 (let ((elements (gethash name (soap-namespace-elements ns))))
344 (cond (discriminant-predicate
345 (catch 'found
346 (dolist (e elements)
347 (when (funcall discriminant-predicate e)
348 (throw 'found e)))))
349 ((= (length elements) 1) (car elements))
350 ((> (length elements) 1)
351 (error
352 "Soap-namespace-get(%s): multiple elements, discriminant needed"
353 name))
354 (t
355 nil))))
356
357
358;;;; WSDL documents
359;;;;; WSDL document elements
360
361(defstruct (soap-basic-type (:include soap-element))
362 kind ; a symbol of: string, dateTime, long, int
363 )
364
365(defstruct soap-sequence-element
366 name type nillable? multiple?)
367
368(defstruct (soap-sequence-type (:include soap-element))
369 parent ; OPTIONAL WSDL-TYPE name
370 elements ; LIST of SOAP-SEQUCENCE-ELEMENT
371 )
372
373(defstruct (soap-array-type (:include soap-element))
374 element-type ; WSDL-TYPE of the array elements
375 )
376
377(defstruct (soap-message (:include soap-element))
378 parts ; ALIST of NAME => WSDL-TYPE name
379 )
380
381(defstruct (soap-operation (:include soap-element))
382 parameter-order
383 input ; (NAME . MESSAGE)
384 output ; (NAME . MESSAGE)
385 faults) ; a list of (NAME . MESSAGE)
386
387(defstruct (soap-port-type (:include soap-element))
388 operations) ; a namespace of operations
389
390;; A bound operation is an operation which has a soap action and a use
391;; method attached -- these are attached as part of a binding and we
392;; can have different bindings for the same operations.
393(defstruct soap-bound-operation
394 operation ; SOAP-OPERATION
395 soap-action ; value for SOAPAction HTTP header
396 use ; 'literal or 'encoded, see
397 ; http://www.w3.org/TR/wsdl#_soap:body
398 )
399
400(defstruct (soap-binding (:include soap-element))
401 port-type
402 (operations (make-hash-table :test 'equal) :readonly t))
403
404(defstruct (soap-port (:include soap-element))
405 service-url
406 binding)
407
408(defun soap-default-xsd-types ()
409 "Return a namespace containing some of the XMLSchema types."
410 (let ((ns (make-soap-namespace :name "http://www.w3.org/2001/XMLSchema")))
411 (dolist (type '("string" "dateTime" "boolean" "long" "int" "float"
412 "base64Binary" "anyType" "Array" "byte[]"))
413 (soap-namespace-put
414 (make-soap-basic-type :name type :kind (intern type))
415 ns))
416 ns))
417
418(defun soap-default-soapenc-types ()
419 "Return a namespace containing some of the SOAPEnc types."
420 (let ((ns (make-soap-namespace
421 :name "http://schemas.xmlsoap.org/soap/encoding/")))
422 (dolist (type '("string" "dateTime" "boolean" "long" "int" "float"
423 "base64Binary" "anyType" "Array" "byte[]"))
424 (soap-namespace-put
425 (make-soap-basic-type :name type :kind (intern type))
426 ns))
427 ns))
428
429(defun soap-type-p (element)
430 "Return t if ELEMENT is a SOAP data type (basic or complex)."
431 (or (soap-basic-type-p element)
432 (soap-sequence-type-p element)
433 (soap-array-type-p element)))
434
435
436;;;;; The WSDL document
437
438;; The WSDL data structure used for encoding/decoding SOAP messages
439(defstruct soap-wsdl
440 origin ; file or URL from which this wsdl was loaded
441 ports ; a list of SOAP-PORT instances
442 alias-table ; a list of namespace aliases
443 namespaces ; a list of namespaces
444 )
445
446(defun soap-wsdl-add-alias (alias name wsdl)
447 "Add a namespace ALIAS for NAME to the WSDL document."
448 (push (cons alias name) (soap-wsdl-alias-table wsdl)))
449
450(defun soap-wsdl-find-namespace (name wsdl)
451 "Find a namespace by NAME in the WSDL document."
452 (catch 'found
453 (dolist (ns (soap-wsdl-namespaces wsdl))
454 (when (equal name (soap-namespace-name ns))
455 (throw 'found ns)))))
456
457(defun soap-wsdl-add-namespace (ns wsdl)
458 "Add the namespace NS to the WSDL document.
459If a namespace by this name already exists in WSDL, individual
460elements will be added to it."
461 (let ((existing (soap-wsdl-find-namespace (soap-namespace-name ns) wsdl)))
462 (if existing
463 ;; Add elements from NS to EXISTING, replacing existing values.
464 (maphash (lambda (key value)
465 (dolist (v value)
466 (soap-namespace-put v existing)))
467 (soap-namespace-elements ns))
468 (push ns (soap-wsdl-namespaces wsdl)))))
469
470(defun soap-wsdl-get (name wsdl &optional predicate use-local-alias-table)
471 "Retrieve element NAME from the WSDL document.
472
473PREDICATE is used to differentiate between elements when NAME
474refers to multiple elements. A typical value for this would be a
475structure predicate for the type of element you want to retrieve.
476For example, to retrieve a message named \"foo\" when other
477elements named \"foo\" exist in the WSDL you could use:
478
479 (soap-wsdl-get \"foo\" WSDL 'soap-message-p)
480
481If USE-LOCAL-ALIAS-TABLE is not nil, `soap-local-xmlns` will be
482used to resolve the namespace alias."
483 (let ((alias-table (soap-wsdl-alias-table wsdl))
484 namespace element-name element)
485
486 (when (symbolp name)
487 (setq name (symbol-name name)))
488
489 (when use-local-alias-table
490 (setq alias-table (append soap-local-xmlns alias-table)))
491
492 (cond ((consp name) ; a fully qualified name, as returned by `soap-l2fq'
493 (setq element-name (cdr name))
494 (when (symbolp element-name)
495 (setq element-name (symbol-name element-name)))
496 (setq namespace (soap-wsdl-find-namespace (car name) wsdl))
497 (unless namespace
498 (error "Soap-wsdl-get(%s): unknown namespace: %s" name namespace)))
499
500 ((string-match "^\\(.*\\):\\(.*\\)$" name)
501 (setq element-name (match-string 2 name))
502
503 (let* ((ns-alias (match-string 1 name))
504 (ns-name (cdr (assoc ns-alias alias-table))))
505 (unless ns-name
506 (error "Soap-wsdl-get(%s): cannot find namespace alias %s"
507 name ns-alias))
508
509 (setq namespace (soap-wsdl-find-namespace ns-name wsdl))
510 (unless namespace
511 (error
512 "Soap-wsdl-get(%s): unknown namespace %s, referenced by alias %s"
513 name ns-name ns-alias))))
514 (t
515 (error "Soap-wsdl-get(%s): bad name" name)))
516
517 (setq element (soap-namespace-get
518 element-name namespace
519 (if predicate
520 (lambda (e)
521 (or (funcall 'soap-namespace-link-p e)
522 (funcall predicate e)))
523 nil)))
524
525 (unless element
526 (error "Soap-wsdl-get(%s): cannot find element" name))
527
528 (if (soap-namespace-link-p element)
529 ;; NOTE: don't use the local alias table here
530 (soap-wsdl-get (soap-namespace-link-target element) wsdl predicate)
531 element)))
532
533;;;;; Resolving references for wsdl types
534
535;; See `soap-wsdl-resolve-references', which is the main entry point for
536;; resolving references
537
538(defun soap-resolve-references-for-element (element wsdl)
539 "Resolve references in ELEMENT using the WSDL document.
540This is a generic function which invokes a specific function
541depending on the element type.
542
543If ELEMENT has no resolver function, it is silently ignored.
544
545All references are resolved in-place, that is the ELEMENT is
546updated."
547 (let ((resolver (get (aref element 0) 'soap-resolve-references)))
548 (when resolver
549 (funcall resolver element wsdl))))
550
551(defun soap-resolve-references-for-sequence-type (type wsdl)
552 "Resolve references for a sequence TYPE using WSDL document.
553See also `soap-resolve-references-for-element' and
554`soap-wsdl-resolve-references'"
555 (let ((parent (soap-sequence-type-parent type)))
556 (when (or (consp parent) (stringp parent))
557 (setf (soap-sequence-type-parent type)
558 (soap-wsdl-get parent wsdl 'soap-type-p))))
559 (dolist (element (soap-sequence-type-elements type))
560 (let ((element-type (soap-sequence-element-type element)))
561 (cond ((or (consp element-type) (stringp element-type))
562 (setf (soap-sequence-element-type element)
563 (soap-wsdl-get element-type wsdl 'soap-type-p)))
564 ((soap-element-p element-type)
565 ;; since the element already has a child element, it
566 ;; could be an inline structure. we must resolve
567 ;; references in it, because it might not be reached by
568 ;; scanning the wsdl names.
569 (soap-resolve-references-for-element element-type wsdl))))))
570
571(defun soap-resolve-references-for-array-type (type wsdl)
572 "Resolve references for an array TYPE using WSDL.
573See also `soap-resolve-references-for-element' and
574`soap-wsdl-resolve-references'"
575 (let ((element-type (soap-array-type-element-type type)))
576 (when (or (consp element-type) (stringp element-type))
577 (setf (soap-array-type-element-type type)
578 (soap-wsdl-get element-type wsdl 'soap-type-p)))))
579
580(defun soap-resolve-references-for-message (message wsdl)
581 "Resolve references for a MESSAGE type using the WSDL document.
582See also `soap-resolve-references-for-element' and
583`soap-wsdl-resolve-references'"
584 (let (resolved-parts)
585 (dolist (part (soap-message-parts message))
586 (let ((name (car part))
587 (type (cdr part)))
588 (when (stringp name)
589 (setq name (intern name)))
590 (when (or (consp type) (stringp type))
591 (setq type (soap-wsdl-get type wsdl 'soap-type-p)))
592 (push (cons name type) resolved-parts)))
593 (setf (soap-message-parts message) (nreverse resolved-parts))))
594
595(defun soap-resolve-references-for-operation (operation wsdl)
596 "Resolve references for an OPERATION type using the WSDL document.
597See also `soap-resolve-references-for-element' and
598`soap-wsdl-resolve-references'"
599 (let ((input (soap-operation-input operation))
600 (counter 0))
601 (let ((name (car input))
602 (message (cdr input)))
603 ;; Name this part if it was not named
604 (when (or (null name) (equal name ""))
605 (setq name (format "in%d" (incf counter))))
606 (when (or (consp message) (stringp message))
607 (setf (soap-operation-input operation)
608 (cons (intern name)
609 (soap-wsdl-get message wsdl 'soap-message-p))))))
610
611 (let ((output (soap-operation-output operation))
612 (counter 0))
613 (let ((name (car output))
614 (message (cdr output)))
615 (when (or (null name) (equal name ""))
616 (setq name (format "out%d" (incf counter))))
617 (when (or (consp message) (stringp message))
618 (setf (soap-operation-output operation)
619 (cons (intern name)
620 (soap-wsdl-get message wsdl 'soap-message-p))))))
621
622 (let ((resolved-faults nil)
623 (counter 0))
624 (dolist (fault (soap-operation-faults operation))
625 (let ((name (car fault))
626 (message (cdr fault)))
627 (when (or (null name) (equal name ""))
628 (setq name (format "fault%d" (incf counter))))
629 (if (or (consp message) (stringp message))
630 (push (cons (intern name)
631 (soap-wsdl-get message wsdl 'soap-message-p))
632 resolved-faults)
633 (push fault resolved-faults))))
634 (setf (soap-operation-faults operation) resolved-faults))
635
636 (when (= (length (soap-operation-parameter-order operation)) 0)
637 (setf (soap-operation-parameter-order operation)
638 (mapcar 'car (soap-message-parts
639 (cdr (soap-operation-input operation))))))
640
641 (setf (soap-operation-parameter-order operation)
642 (mapcar (lambda (p)
643 (if (stringp p)
644 (intern p)
645 p))
646 (soap-operation-parameter-order operation))))
647
648(defun soap-resolve-references-for-binding (binding wsdl)
649 "Resolve references for a BINDING type using the WSDL document.
650See also `soap-resolve-references-for-element' and
651`soap-wsdl-resolve-references'"
652 (when (or (consp (soap-binding-port-type binding))
653 (stringp (soap-binding-port-type binding)))
654 (setf (soap-binding-port-type binding)
655 (soap-wsdl-get (soap-binding-port-type binding)
656 wsdl 'soap-port-type-p)))
657
658 (let ((port-ops (soap-port-type-operations (soap-binding-port-type binding))))
659 (maphash (lambda (k v)
660 (setf (soap-bound-operation-operation v)
661 (soap-namespace-get k port-ops 'soap-operation-p)))
662 (soap-binding-operations binding))))
663
664(defun soap-resolve-references-for-port (port wsdl)
665 "Resolve references for a PORT type using the WSDL document.
666See also `soap-resolve-references-for-element' and
667`soap-wsdl-resolve-references'"
668 (when (or (consp (soap-port-binding port))
669 (stringp (soap-port-binding port)))
670 (setf (soap-port-binding port)
671 (soap-wsdl-get (soap-port-binding port) wsdl 'soap-binding-p))))
672
673;; Install resolvers for our types
674(progn
675 (put (aref (make-soap-sequence-type) 0) 'soap-resolve-references
676 'soap-resolve-references-for-sequence-type)
677 (put (aref (make-soap-array-type) 0) 'soap-resolve-references
678 'soap-resolve-references-for-array-type)
679 (put (aref (make-soap-message) 0) 'soap-resolve-references
680 'soap-resolve-references-for-message)
681 (put (aref (make-soap-operation) 0) 'soap-resolve-references
682 'soap-resolve-references-for-operation)
683 (put (aref (make-soap-binding) 0) 'soap-resolve-references
684 'soap-resolve-references-for-binding)
685 (put (aref (make-soap-port) 0) 'soap-resolve-references
686 'soap-resolve-references-for-port))
687
688(defun soap-wsdl-resolve-references (wsdl)
689 "Resolve all references inside the WSDL structure.
690
691When the WSDL elements are created from the XML document, they
692refer to each other by name. For example, the ELEMENT-TYPE slot
693of an SOAP-ARRAY-TYPE will contain the name of the element and
694the user would have to call `soap-wsdl-get' to obtain the actual
695element.
696
697After the entire document is loaded, we resolve all these
698references to the actual elements they refer to so that at
699runtime, we don't have to call `soap-wsdl-get' each time we
700traverse an element tree."
701 (let ((nprocessed 0)
702 (nstag-id 0)
703 (alias-table (soap-wsdl-alias-table wsdl)))
704 (dolist (ns (soap-wsdl-namespaces wsdl))
705 (let ((nstag (car-safe (rassoc (soap-namespace-name ns) alias-table))))
706 (unless nstag
707 ;; If this namespace does not have an alias, create one for it.
708 (catch 'done
709 (while t
710 (setq nstag (format "ns%d" (incf nstag-id)))
711 (unless (assoc nstag alias-table)
712 (soap-wsdl-add-alias nstag (soap-namespace-name ns) wsdl)
713 (throw 'done t)))))
714
715 (maphash (lambda (name element)
716 (cond ((soap-element-p element) ; skip links
717 (incf nprocessed)
718 (soap-resolve-references-for-element element wsdl)
719 (setf (soap-element-namespace-tag element) nstag))
720 ((listp element)
721 (dolist (e element)
722 (when (soap-element-p e)
723 (incf nprocessed)
724 (soap-resolve-references-for-element e wsdl)
725 (setf (soap-element-namespace-tag e) nstag))))))
726 (soap-namespace-elements ns))))
727
728 (message "Processed %d" nprocessed))
729 wsdl)
730
731;;;;; Loading WSDL from XML documents
732
733(defun soap-load-wsdl-from-url (url)
734 "Load a WSDL document from URL and return it.
735The returned WSDL document needs to be used for `soap-invoke'
736calls."
737 (let ((url-request-method "GET")
738 (url-package-name "soap-client.el")
739 (url-package-version "1.0")
740 (url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
741 (url-request-coding-system 'utf-8)
742 (url-http-attempt-keepalives nil))
743 (let ((buffer (url-retrieve-synchronously url)))
744 (with-current-buffer buffer
745 (declare (special url-http-response-status))
746 (if (> url-http-response-status 299)
747 (error "Error retrieving WSDL: %s" url-http-response-status))
748 (let ((mime-part (mm-dissect-buffer t t)))
749 (unless mime-part
750 (error "Failed to decode response from server"))
751 (unless (equal (car (mm-handle-type mime-part)) "text/xml")
752 (error "Server response is not an XML document"))
753 (with-temp-buffer
754 (mm-insert-part mime-part)
755 (let ((wsdl-xml (car (xml-parse-region (point-min) (point-max)))))
756 (prog1
757 (let ((wsdl (soap-parse-wsdl wsdl-xml)))
758 (setf (soap-wsdl-origin wsdl) url)
759 wsdl)
760 (kill-buffer buffer)))))))))
761
762(defun soap-load-wsdl (file)
763 "Load a WSDL document from FILE and return it."
764 (with-temp-buffer
765 (insert-file-contents file)
766 (let ((xml (car (xml-parse-region (point-min) (point-max)))))
767 (let ((wsdl (soap-parse-wsdl xml)))
768 (setf (soap-wsdl-origin wsdl) file)
769 wsdl))))
770
771(defun soap-parse-wsdl (node)
772 "Construct a WSDL structure from NODE, which is an XML document."
773 (soap-with-local-xmlns node
774
775 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:definitions)
776 nil
777 "soap-parse-wsdl: expecting wsdl:definitions node, got %s"
778 (soap-l2wk (xml-node-name node)))
779
780 (let ((wsdl (make-soap-wsdl)))
781
782 ;; Add the local alias table to the wsdl document -- it will be used for
783 ;; all types in this document even after we finish parsing it.
784 (setf (soap-wsdl-alias-table wsdl) soap-local-xmlns)
785
786 ;; Add the XSD types to the wsdl document
787 (let ((ns (soap-default-xsd-types)))
788 (soap-wsdl-add-namespace ns wsdl)
789 (soap-wsdl-add-alias "xsd" (soap-namespace-name ns) wsdl))
790
791 ;; Add the soapenc types to the wsdl document
792 (let ((ns (soap-default-soapenc-types)))
793 (soap-wsdl-add-namespace ns wsdl)
794 (soap-wsdl-add-alias "soapenc" (soap-namespace-name ns) wsdl))
795
796 ;; Find all the 'xsd:schema nodes which are children of wsdl:types nodes
797 ;; and build our type-library
798
799 (let ((types (car (soap-xml-get-children1 node 'wsdl:types))))
800 (dolist (node (xml-node-children types))
801 ;; We cannot use (xml-get-children node (soap-wk2l 'xsd:schema))
802 ;; because each node can install its own alias type so the schema
803 ;; nodes might have a different prefix.
804 (when (consp node)
805 (soap-with-local-xmlns node
806 (when (eq (soap-l2wk (xml-node-name node)) 'xsd:schema)
807 (soap-wsdl-add-namespace (soap-parse-schema node) wsdl))))))
808
809 (let ((ns (make-soap-namespace :name (soap-get-target-namespace node))))
810 (dolist (node (soap-xml-get-children1 node 'wsdl:message))
811 (soap-namespace-put (soap-parse-message node) ns))
812
813 (dolist (node (soap-xml-get-children1 node 'wsdl:portType))
814 (let ((port-type (soap-parse-port-type node)))
815 (soap-namespace-put port-type ns)
816 (soap-wsdl-add-namespace
817 (soap-port-type-operations port-type) wsdl)))
818
819 (dolist (node (soap-xml-get-children1 node 'wsdl:binding))
820 (soap-namespace-put (soap-parse-binding node) ns))
821
822 (dolist (node (soap-xml-get-children1 node 'wsdl:service))
823 (dolist (node (soap-xml-get-children1 node 'wsdl:port))
824 (let ((name (xml-get-attribute node 'name))
825 (binding (xml-get-attribute node 'binding))
826 (url (let ((n (car (soap-xml-get-children1
827 node 'wsdlsoap:address))))
828 (xml-get-attribute n 'location))))
829 (let ((port (make-soap-port
830 :name name :binding (soap-l2fq binding 'tns)
831 :service-url url)))
832 (soap-namespace-put port ns)
833 (push port (soap-wsdl-ports wsdl))))))
834
835 (soap-wsdl-add-namespace ns wsdl))
836
837 (soap-wsdl-resolve-references wsdl)
838
839 wsdl)))
840
841(defun soap-parse-schema (node)
842 "Parse a schema NODE.
843Return a SOAP-NAMESPACE containing the elements."
844 (soap-with-local-xmlns node
845 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:schema)
846 nil
847 "soap-parse-schema: expecting an xsd:schema node, got %s"
848 (soap-l2wk (xml-node-name node)))
849 (let ((ns (make-soap-namespace :name (soap-get-target-namespace node))))
850 ;; NOTE: we only extract the complexTypes from the schema, we wouldn't
851 ;; know how to handle basic types beyond the built in ones anyway.
852 (dolist (node (soap-xml-get-children1 node 'xsd:complexType))
853 (soap-namespace-put (soap-parse-complex-type node) ns))
854
855 (dolist (node (soap-xml-get-children1 node 'xsd:element))
856 (soap-namespace-put (soap-parse-schema-element node) ns))
857
858 ns)))
859
860(defun soap-parse-schema-element (node)
861 "Parse NODE and construct a schema element from it."
862 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:element)
863 nil
864 "soap-parse-schema-element: expecting xsd:element node, got %s"
865 (soap-l2wk (xml-node-name node)))
866 (let ((name (xml-get-attribute-or-nil node 'name))
867 type)
868 ;; A schema element that contains an inline complex type --
869 ;; construct the actual complex type for it.
870 (let ((type-node (soap-xml-get-children1 node 'xsd:complexType)))
871 (when (> (length type-node) 0)
872 (assert (= (length type-node) 1)) ; only one complex type
873 ; definition per element
874 (setq type (soap-parse-complex-type (car type-node)))))
875 (setf (soap-element-name type) name)
876 type))
877
878(defun soap-parse-complex-type (node)
879 "Parse NODE and construct a complex type from it."
880 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:complexType)
881 nil
882 "soap-parse-complex-type: expecting xsd:complexType node, got %s"
883 (soap-l2wk (xml-node-name node)))
884 (let ((name (xml-get-attribute-or-nil node 'name))
885 ;; Use a dummy type for the complex type, it will be replaced
886 ;; with the real type below, except when the complex type node
887 ;; is empty...
888 (type (make-soap-sequence-type :elements nil)))
889 (dolist (c (xml-node-children node))
890 (when (consp c) ; skip string nodes, which are whitespace
891 (let ((node-name (soap-l2wk (xml-node-name c))))
892 (cond
893 ((eq node-name 'xsd:sequence)
894 (setq type (soap-parse-complex-type-sequence c)))
895 ((eq node-name 'xsd:complexContent)
896 (setq type (soap-parse-complex-type-complex-content c)))
897 ((eq node-name 'xsd:attribute)
898 ;; The name of this node comes from an attribute tag
899 (let ((n (xml-get-attribute-or-nil c 'name)))
900 (setq name n)))
901 (t
902 (error "Unknown node type %s" node-name))))))
903 (setf (soap-element-name type) name)
904 type))
905
906(defun soap-parse-sequence (node)
907 "Parse NODE and a list of sequence elements that it defines.
908NODE is assumed to be an xsd:sequence node. In that case, each
909of its children is assumed to be a sequence element. Each
910sequence element is parsed constructing the corresponding type.
911A list of these types is returned."
912 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:sequence)
913 nil
914 "soap-parse-sequence: expecting xsd:sequence node, got %s"
915 (soap-l2wk (xml-node-name node)))
916 (let (elements)
917 (dolist (e (soap-xml-get-children1 node 'xsd:element))
918 (let ((name (xml-get-attribute-or-nil e 'name))
919 (type (xml-get-attribute-or-nil e 'type))
920 (nillable? (or (equal (xml-get-attribute-or-nil e 'nillable) "true")
921 (let ((e (xml-get-attribute-or-nil e 'minOccurs)))
922 (and e (equal e "0")))))
923 (multiple? (let ((e (xml-get-attribute-or-nil e 'maxOccurs)))
924 (and e (not (equal e "1"))))))
925 (if type
926 (setq type (soap-l2fq type 'tns))
927
928 ;; The node does not have a type, maybe it has a complexType
929 ;; defined inline...
930 (let ((type-node (soap-xml-get-children1 e 'xsd:complexType)))
931 (when (> (length type-node) 0)
932 (assert (= (length type-node) 1)
933 nil
934 "only one complex type definition per element supported")
935 (setq type (soap-parse-complex-type (car type-node))))))
936
937 (push (make-soap-sequence-element
938 :name (intern name) :type type :nillable? nillable?
939 :multiple? multiple?)
940 elements)))
941 (nreverse elements)))
942
943(defun soap-parse-complex-type-sequence (node)
944 "Parse NODE as a sequence type."
945 (let ((elements (soap-parse-sequence node)))
946 (make-soap-sequence-type :elements elements)))
947
948(defun soap-parse-complex-type-complex-content (node)
949 "Parse NODE as a xsd:complexContent node.
950A sequence or an array type is returned depending on the actual
951contents."
952 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:complexContent)
953 nil
954 "soap-parse-complex-type-complex-content: expecting xsd:complexContent node, got %s"
955 (soap-l2wk (xml-node-name node)))
956 (let (array? parent elements)
957 (let ((extension (car-safe (soap-xml-get-children1 node 'xsd:extension)))
958 (restriction (car-safe
959 (soap-xml-get-children1 node 'xsd:restriction))))
960 ;; a complex content node is either an extension or a restriction
961 (cond (extension
962 (setq parent (xml-get-attribute-or-nil extension 'base))
963 (setq elements (soap-parse-sequence
964 (car (soap-xml-get-children1
965 extension 'xsd:sequence)))))
966 (restriction
967 (let ((base (xml-get-attribute-or-nil restriction 'base)))
968 (assert (equal base "soapenc:Array")
969 nil
970 "restrictions supported only for soapenc:Array types, this is a %s"
971 base))
972 (setq array? t)
973 (let ((attribute (car (soap-xml-get-children1
974 restriction 'xsd:attribute))))
975 (let ((array-type (soap-xml-get-attribute-or-nil1
976 attribute 'wsdl:arrayType)))
977 (when (string-match "^\\(.*\\)\\[\\]$" array-type)
978 (setq parent (match-string 1 array-type))))))
979
980 (t
981 (error "Unknown complex type"))))
982
983 (if parent
984 (setq parent (soap-l2fq parent 'tns)))
985
986 (if array?
987 (make-soap-array-type :element-type parent)
988 (make-soap-sequence-type :parent parent :elements elements))))
989
990(defun soap-parse-message (node)
991 "Parse NODE as a wsdl:message and return the corresponding type."
992 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:message)
993 nil
994 "soap-parse-message: expecting wsdl:message node, got %s"
995 (soap-l2wk (xml-node-name node)))
996 (let ((name (xml-get-attribute-or-nil node 'name))
997 parts)
998 (dolist (p (soap-xml-get-children1 node 'wsdl:part))
999 (let ((name (xml-get-attribute-or-nil p 'name))
1000 (type (xml-get-attribute-or-nil p 'type))
1001 (element (xml-get-attribute-or-nil p 'element)))
1002
1003 (when type
1004 (setq type (soap-l2fq type 'tns)))
1005
1006 (when element
1007 (setq element (soap-l2fq element 'tns)))
1008
1009 (push (cons name (or type element)) parts)))
1010 (make-soap-message :name name :parts (nreverse parts))))
1011
1012(defun soap-parse-port-type (node)
1013 "Parse NODE as a wsdl:portType and return the corresponding port."
1014 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:portType)
1015 nil
1016 "soap-parse-port-type: expecting wsdl:portType node got %s"
1017 (soap-l2wk (xml-node-name node)))
1018 (let ((ns (make-soap-namespace
1019 :name (concat "urn:" (xml-get-attribute node 'name)))))
1020 (dolist (node (soap-xml-get-children1 node 'wsdl:operation))
1021 (let ((o (soap-parse-operation node)))
1022
1023 (let ((other-operation (soap-namespace-get
1024 (soap-element-name o) ns 'soap-operation-p)))
1025 (if other-operation
1026 ;; Unfortunately, the Confluence WSDL defines two operations
1027 ;; named "search" which differ only in parameter names...
1028 (soap-warning "Discarding duplicate operation: %s"
1029 (soap-element-name o))
1030
1031 (progn
1032 (soap-namespace-put o ns)
1033
1034 ;; link all messages from this namespace, as this namespace
1035 ;; will be used for decoding the response.
1036 (destructuring-bind (name . message) (soap-operation-input o)
1037 (soap-namespace-put-link name message ns))
1038
1039 (destructuring-bind (name . message) (soap-operation-output o)
1040 (soap-namespace-put-link name message ns))
1041
1042 (dolist (fault (soap-operation-faults o))
1043 (destructuring-bind (name . message) fault
1044 (soap-namespace-put-link name message ns 'replace)))
1045
1046 )))))
1047
1048 (make-soap-port-type :name (xml-get-attribute node 'name)
1049 :operations ns)))
1050
1051(defun soap-parse-operation (node)
1052 "Parse NODE as a wsdl:operation and return the corresponding type."
1053 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:operation)
1054 nil
1055 "soap-parse-operation: expecting wsdl:operation node, got %s"
1056 (soap-l2wk (xml-node-name node)))
1057 (let ((name (xml-get-attribute node 'name))
1058 (parameter-order (split-string
1059 (xml-get-attribute node 'parameterOrder)))
1060 input output faults)
1061 (dolist (n (xml-node-children node))
1062 (when (consp n) ; skip string nodes which are whitespace
1063 (let ((node-name (soap-l2wk (xml-node-name n))))
1064 (cond
1065 ((eq node-name 'wsdl:input)
1066 (let ((message (xml-get-attribute n 'message))
1067 (name (xml-get-attribute n 'name)))
1068 (setq input (cons name (soap-l2fq message 'tns)))))
1069 ((eq node-name 'wsdl:output)
1070 (let ((message (xml-get-attribute n 'message))
1071 (name (xml-get-attribute n 'name)))
1072 (setq output (cons name (soap-l2fq message 'tns)))))
1073 ((eq node-name 'wsdl:fault)
1074 (let ((message (xml-get-attribute n 'message))
1075 (name (xml-get-attribute n 'name)))
1076 (push (cons name (soap-l2fq message 'tns)) faults)))))))
1077 (make-soap-operation
1078 :name name
1079 :parameter-order parameter-order
1080 :input input
1081 :output output
1082 :faults (nreverse faults))))
1083
1084(defun soap-parse-binding (node)
1085 "Parse NODE as a wsdl:binding and return the corresponding type."
1086 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:binding)
1087 nil
1088 "soap-parse-binding: expecting wsdl:binding node, got %s"
1089 (soap-l2wk (xml-node-name node)))
1090 (let ((name (xml-get-attribute node 'name))
1091 (type (xml-get-attribute node 'type)))
1092 (let ((binding (make-soap-binding :name name
1093 :port-type (soap-l2fq type 'tns))))
1094 (dolist (wo (soap-xml-get-children1 node 'wsdl:operation))
1095 (let ((name (xml-get-attribute wo 'name))
1096 soap-action
1097 use)
1098 (dolist (so (soap-xml-get-children1 wo 'wsdlsoap:operation))
1099 (setq soap-action (xml-get-attribute-or-nil so 'soapAction)))
1100
1101 ;; Search a wsdlsoap:body node and find a "use" tag. The
1102 ;; same use tag is assumed to be present for both input and
1103 ;; output types (although the WDSL spec allows separate
1104 ;; "use"-s for each of them...
1105
1106 (dolist (i (soap-xml-get-children1 wo 'wsdl:input))
1107 (dolist (b (soap-xml-get-children1 i 'wsdlsoap:body))
1108 (setq use (or use
1109 (xml-get-attribute-or-nil b 'use)))))
1110
1111 (unless use
1112 (dolist (i (soap-xml-get-children1 wo 'wsdl:output))
1113 (dolist (b (soap-xml-get-children1 i 'wsdlsoap:body))
1114 (setq use (or use
1115 (xml-get-attribute-or-nil b 'use))))))
1116
1117 (puthash name (make-soap-bound-operation :operation name
1118 :soap-action soap-action
1119 :use (and use (intern use)))
1120 (soap-binding-operations binding))))
1121 binding)))
1122
1123;;;; SOAP type decoding
1124
1125(defvar soap-multi-refs nil
1126 "The list of multi-ref nodes in the current SOAP response.
1127This is a dynamically bound variable used during decoding the
1128SOAP response.")
1129
1130(defvar soap-decoded-multi-refs nil
1131 "List of decoded multi-ref nodes in the current SOAP response.
1132This is a dynamically bound variable used during decoding the
1133SOAP response.")
1134
1135(defvar soap-current-wsdl nil
1136 "The current WSDL document used when decoding the SOAP response.
1137This is a dynamically bound variable.")
1138
1139(defun soap-decode-type (type node)
1140 "Use TYPE (an xsd type) to decode the contents of NODE.
1141
1142NODE is an XML node, representing some SOAP encoded value or a
1143reference to another XML node (a multiRef). This function will
1144resolve the multiRef reference, if any, than call a TYPE specific
1145decode function to perform the actual decoding."
1146 (let ((href (xml-get-attribute-or-nil node 'href)))
1147 (cond (href
1148 (catch 'done
1149 ;; NODE is actually a HREF, find the target and decode that.
1150 ;; Check first if we already decoded this multiref.
1151
1152 (let ((decoded (cdr (assoc href soap-decoded-multi-refs))))
1153 (when decoded
1154 (throw 'done decoded)))
1155
1156 (string-match "^#\\(.*\\)$" href) ; TODO: check that it matched
1157
1158 (let ((id (match-string 1 href)))
1159 (dolist (mr soap-multi-refs)
1160 (let ((mrid (xml-get-attribute mr 'id)))
1161 (when (equal id mrid)
1162 ;; recurse here, in case there are multiple HREF's
1163 (let ((decoded (soap-decode-type type mr)))
1164 (push (cons href decoded) soap-decoded-multi-refs)
1165 (throw 'done decoded)))))
1166 (error "Cannot find href %s" href))))
1167 (t
1168 (soap-with-local-xmlns node
1169 (if (equal (soap-xml-get-attribute-or-nil1 node 'xsi:nil) "true")
1170 nil
1171 (let ((decoder (get (aref type 0) 'soap-decoder)))
1172 (assert decoder nil "no soap-decoder for %s type"
1173 (aref type 0))
1174 (funcall decoder type node))))))))
1175
1176(defun soap-decode-any-type (node)
1177 "Decode NODE using type information inside it."
1178 ;; If the NODE has type information, we use that...
1179 (let ((type (soap-xml-get-attribute-or-nil1 node 'xsi:type)))
1180 (if type
1181 (let ((wtype (soap-wsdl-get type soap-current-wsdl 'soap-type-p)))
1182 (if wtype
1183 (soap-decode-type wtype node)
1184 ;; The node has type info encoded in it, but we don't know how
1185 ;; to decode it...
1186 (error "Soap-decode-any-type: node has unknown type: %s" type)))
1187
1188 ;; No type info in the node...
1189
1190 (let ((contents (xml-node-children node)))
1191 (if (and (= (length contents) 1) (stringp (car contents)))
1192 ;; contents is just a string
1193 (car contents)
1194
1195 ;; we assume the NODE is a sequence with every element a
1196 ;; structure name
1197 (let (result)
1198 (dolist (element contents)
1199 (let ((key (xml-node-name element))
1200 (value (soap-decode-any-type element)))
1201 (push (cons key value) result)))
1202 (nreverse result)))))))
1203
1204(defun soap-decode-array (node)
1205 "Decode NODE as an Array using type information inside it."
1206 (let ((type (soap-xml-get-attribute-or-nil1 node 'soapenc:arrayType))
1207 (wtype nil)
1208 (contents (xml-node-children node))
1209 result)
1210 (when type
1211 ;; Type is in the format "someType[NUM]" where NUM is the number of
1212 ;; elements in the array. We discard the [NUM] part.
1213 (setq type (replace-regexp-in-string "\\[[0-9]+\\]\\'" "" type))
1214 (setq wtype (soap-wsdl-get type soap-current-wsdl 'soap-type-p))
1215 (unless wtype
1216 ;; The node has type info encoded in it, but we don't know how to
1217 ;; decode it...
1218 (error "Soap-decode-array: node has unknown type: %s" type)))
1219 (dolist (e contents)
1220 (when (consp e)
1221 (push (if wtype
1222 (soap-decode-type wtype e)
1223 (soap-decode-any-type e))
1224 result)))
1225 (nreverse result)))
1226
1227(defun soap-decode-basic-type (type node)
1228 "Use TYPE to decode the contents of NODE.
1229TYPE is a `soap-basic-type' struct, and NODE is an XML document.
1230A LISP value is returned based on the contents of NODE and the
1231type-info stored in TYPE."
1232 (let ((contents (xml-node-children node))
1233 (type-kind (soap-basic-type-kind type)))
1234
1235 (if (null contents)
1236 nil
1237 (ecase type-kind
1238 (string (car contents))
1239 (dateTime (car contents)) ; TODO: convert to a date time
1240 ((long int float) (string-to-number (car contents)))
1241 (boolean (string= (downcase (car contents)) "true"))
1242 (base64Binary (base64-decode-string (car contents)))
1243 (anyType (soap-decode-any-type node))
1244 (Array (soap-decode-array node))))))
1245
1246(defun soap-decode-sequence-type (type node)
1247 "Use TYPE to decode the contents of NODE.
1248TYPE is assumed to be a sequence type and an ALIST with the
1249contents of the NODE is returned."
1250 (let ((result nil)
1251 (parent (soap-sequence-type-parent type)))
1252 (when parent
1253 (setq result (nreverse (soap-decode-type parent node))))
1254 (dolist (element (soap-sequence-type-elements type))
1255 (let ((instance-count 0)
1256 (e-name (soap-sequence-element-name element))
1257 (e-type (soap-sequence-element-type element)))
1258 (dolist (node (xml-get-children node e-name))
1259 (incf instance-count)
1260 (push (cons e-name (soap-decode-type e-type node)) result))
1261 ;; Do some sanity checking
1262 (cond ((and (= instance-count 0)
1263 (not (soap-sequence-element-nillable? element)))
1264 (soap-warning "While decoding %s: missing non-nillable slot %s"
1265 (soap-element-name type) e-name))
1266 ((and (> instance-count 1)
1267 (not (soap-sequence-element-multiple? element)))
1268 (soap-warning "While decoding %s: multiple slots named %s"
1269 (soap-element-name type) e-name)))))
1270 (nreverse result)))
1271
1272(defun soap-decode-array-type (type node)
1273 "Use TYPE to decode the contents of NODE.
1274TYPE is assumed to be an array type. Arrays are decoded as lists.
1275This is because it is easier to work with list results in LISP."
1276 (let ((result nil)
1277 (element-type (soap-array-type-element-type type)))
1278 (dolist (node (xml-node-children node))
1279 (when (consp node)
1280 (push (soap-decode-type element-type node) result)))
1281 (nreverse result)))
1282
1283(progn
1284 (put (aref (make-soap-basic-type) 0)
1285 'soap-decoder 'soap-decode-basic-type)
1286 (put (aref (make-soap-sequence-type) 0)
1287 'soap-decoder 'soap-decode-sequence-type)
1288 (put (aref (make-soap-array-type) 0)
1289 'soap-decoder 'soap-decode-array-type))
1290
1291;;;; Soap Envelope parsing
1292
1293(put 'soap-error
1294 'error-conditions
1295 '(error soap-error))
1296(put 'soap-error 'error-message "SOAP error")
1297
1298(defun soap-parse-envelope (node operation wsdl)
1299 "Parse the SOAP envelope in NODE and return the response.
1300OPERATION is the WSDL operation for which we expect the response,
1301WSDL is used to decode the NODE"
1302 (soap-with-local-xmlns node
1303 (assert (eq (soap-l2wk (xml-node-name node)) 'soap:Envelope)
1304 nil
1305 "soap-parse-envelope: expecting soap:Envelope node, got %s"
1306 (soap-l2wk (xml-node-name node)))
1307 (let ((body (car (soap-xml-get-children1 node 'soap:Body))))
1308
1309 (let ((fault (car (soap-xml-get-children1 body 'soap:Fault))))
1310 (when fault
1311 (let ((fault-code (let ((n (car (xml-get-children
1312 fault 'faultcode))))
1313 (car-safe (xml-node-children n))))
1314 (fault-string (let ((n (car (xml-get-children
1315 fault 'faultstring))))
1316 (car-safe (xml-node-children n)))))
1317 (while t
1318 (signal 'soap-error (list fault-code fault-string))))))
1319
1320 ;; First (non string) element of the body is the root node of he
1321 ;; response
1322 (let ((response (if (eq (soap-bound-operation-use operation) 'literal)
1323 ;; For 'literal uses, the response is the actual body
1324 body
1325 ;; ...otherwise the first non string element
1326 ;; of the body is the response
1327 (catch 'found
1328 (dolist (n (xml-node-children body))
1329 (when (consp n)
1330 (throw 'found n)))))))
1331 (soap-parse-response response operation wsdl body)))))
1332
1333(defun soap-parse-response (response-node operation wsdl soap-body)
1334 "Parse RESPONSE-NODE and return the result as a LISP value.
1335OPERATION is the WSDL operation for which we expect the response,
1336WSDL is used to decode the NODE.
1337
1338SOAP-BODY is the body of the SOAP envelope (of which
1339RESPONSE-NODE is a sub-node). It is used in case RESPONSE-NODE
1340reference multiRef parts which are external to RESPONSE-NODE."
1341 (let* ((soap-current-wsdl wsdl)
1342 (op (soap-bound-operation-operation operation))
1343 (use (soap-bound-operation-use operation))
1344 (message (cdr (soap-operation-output op))))
1345
1346 (soap-with-local-xmlns response-node
1347
1348 (when (eq use 'encoded)
1349 (let* ((received-message-name (soap-l2fq (xml-node-name response-node)))
1350 (received-message (soap-wsdl-get
1351 received-message-name wsdl 'soap-message-p)))
1352 (unless (eq received-message message)
1353 (error "Unexpected message: got %s, expecting %s"
1354 received-message-name
1355 (soap-element-name message)))))
1356
1357 (let ((decoded-parts nil)
1358 (soap-multi-refs (xml-get-children soap-body 'multiRef))
1359 (soap-decoded-multi-refs nil))
1360
1361 (dolist (part (soap-message-parts message))
1362 (let ((tag (car part))
1363 (type (cdr part))
1364 node)
1365
1366 (setq node
1367 (cond
1368 ((eq use 'encoded)
1369 (car (xml-get-children response-node tag)))
1370
1371 ((eq use 'literal)
1372 (catch 'found
1373 (let* ((ns-aliases (soap-wsdl-alias-table wsdl))
1374 (ns-name (cdr (assoc
1375 (soap-element-namespace-tag type)
1376 ns-aliases)))
1377 (fqname (cons ns-name (soap-element-name type))))
1378 (dolist (c (xml-node-children response-node))
1379 (when (consp c)
1380 (soap-with-local-xmlns c
1381 (when (equal (soap-l2fq (xml-node-name c))
1382 fqname)
1383 (throw 'found c))))))))))
1384
1385 (unless node
1386 (error "Soap-parse-response(%s): cannot find message part %s"
1387 (soap-element-name op) tag))
1388 (push (soap-decode-type type node) decoded-parts)))
1389
1390 decoded-parts))))
1391
1392;;;; SOAP type encoding
1393
1394(defvar soap-encoded-namespaces nil
1395 "A list of namespace tags used during encoding a message.
1396This list is populated by `soap-encode-value' and used by
1397`soap-create-envelope' to add aliases for these namespace to the
1398XML request.
1399
1400This variable is dynamically bound in `soap-create-envelope'.")
1401
1402(defun soap-encode-value (xml-tag value type)
1403 "Encode inside an XML-TAG the VALUE using TYPE.
1404The resulting XML data is inserted in the current buffer
1405at (point)/
1406
1407TYPE is one of the soap-*-type structures which defines how VALUE
1408is to be encoded. This is a generic function which finds an
1409encoder function based on TYPE and calls that encoder to do the
1410work."
1411 (let ((encoder (get (aref type 0) 'soap-encoder)))
1412 (assert encoder nil "no soap-encoder for %s type" (aref type 0))
1413 ;; XML-TAG can be a string or a symbol, but we pass only string's to the
1414 ;; encoders
1415 (when (symbolp xml-tag)
1416 (setq xml-tag (symbol-name xml-tag)))
1417 (funcall encoder xml-tag value type))
1418 (add-to-list 'soap-encoded-namespaces (soap-element-namespace-tag type)))
1419
1420(defun soap-encode-basic-type (xml-tag value type)
1421 "Encode inside XML-TAG the LISP VALUE according to TYPE.
1422Do not call this function directly, use `soap-encode-value'
1423instead."
1424 (let ((xsi-type (soap-element-fq-name type))
1425 (basic-type (soap-basic-type-kind type)))
1426
1427 ;; try to classify the type based on the value type and use that type when
1428 ;; encoding
1429 (when (eq basic-type 'anyType)
1430 (cond ((stringp value)
1431 (setq xsi-type "xsd:string" basic-type 'string))
1432 ((integerp value)
1433 (setq xsi-type "xsd:int" basic-type 'int))
1434 ((memq value '(t nil))
1435 (setq xsi-type "xsd:boolean" basic-type 'boolean))
1436 (t
1437 (error
1438 "Soap-encode-basic-type(%s, %s, %s): cannot classify anyType value"
1439 xml-tag value xsi-type))))
1440
1441 (insert "<" xml-tag " xsi:type=\"" xsi-type "\"")
1442
1443 ;; We have some ambiguity here, as a nil value represents "false" when the
1444 ;; type is boolean, we will never have a "nil" boolean type...
1445
1446 (if (or value (eq basic-type 'boolean))
1447 (progn
1448 (insert ">")
1449 (case basic-type
1450 (string
1451 (unless (stringp value)
1452 (error "Soap-encode-basic-type(%s, %s, %s): not a string value"
1453 xml-tag value xsi-type))
1454 (insert (url-insert-entities-in-string value)))
1455
1456 (dateTime
1457 (cond ((and (consp value) ; is there a time-value-p ?
1458 (>= (length value) 2)
1459 (numberp (nth 0 value))
1460 (numberp (nth 1 value)))
1461 ;; Value is a (current-time) style value, convert
1462 ;; to a string
1463 (insert (format-time-string "%Y-%m-%dT%H:%M:%S" value)))
1464 ((stringp value)
1465 (insert (url-insert-entities-in-string value)))
1466 (t
1467 (error
1468 "Soap-encode-basic-type(%s, %s, %s): not a dateTime value"
1469 xml-tag value xsi-type))))
1470
1471 (boolean
1472 (unless (memq value '(t nil))
1473 (error "Soap-encode-basic-type(%s, %s, %s): not a boolean value"
1474 xml-tag value xsi-type))
1475 (insert (if value "true" "false")))
1476
1477 ((long int)
1478 (unless (integerp value)
1479 (error "Soap-encode-basic-type(%s, %s, %s): not an integer value"
1480 xml-tag value xsi-type))
1481 (insert (number-to-string value)))
1482
1483 (base64Binary
1484 (unless (stringp value)
1485 (error "Soap-encode-basic-type(%s, %s, %s): not a string value"
1486 xml-tag value xsi-type))
1487 (insert (base64-encode-string value)))
1488
1489 (otherwise
1490 (error
1491 "Soap-encode-basic-type(%s, %s, %s): don't know how to encode"
1492 xml-tag value xsi-type))))
1493
1494 (insert " xsi:nil=\"true\">"))
1495 (insert "</" xml-tag ">\n")))
1496
1497(defun soap-encode-sequence-type (xml-tag value type)
1498 "Encode inside XML-TAG the LISP VALUE according to TYPE.
1499Do not call this function directly, use `soap-encode-value'
1500instead."
1501 (let ((xsi-type (soap-element-fq-name type)))
1502 (insert "<" xml-tag " xsi:type=\"" xsi-type "\"")
1503 (if value
1504 (progn
1505 (insert ">\n")
1506 (let ((parents (list type))
1507 (parent (soap-sequence-type-parent type)))
1508
1509 (while parent
1510 (push parent parents)
1511 (setq parent (soap-sequence-type-parent parent)))
1512
1513 (dolist (type parents)
1514 (dolist (element (soap-sequence-type-elements type))
1515 (let ((instance-count 0)
1516 (e-name (soap-sequence-element-name element))
1517 (e-type (soap-sequence-element-type element)))
1518 (dolist (v value)
1519 (when (equal (car v) e-name)
1520 (incf instance-count)
1521 (soap-encode-value e-name (cdr v) e-type)))
1522
1523 ;; Do some sanity checking
1524 (cond ((and (= instance-count 0)
1525 (not (soap-sequence-element-nillable? element)))
1526 (soap-warning
1527 "While encoding %s: missing non-nillable slot %s"
1528 (soap-element-name type) e-name))
1529 ((and (> instance-count 1)
1530 (not (soap-sequence-element-multiple? element)))
1531 (soap-warning
1532 "While encoding %s: multiple slots named %s"
1533 (soap-element-name type) e-name))))))))
1534 (insert " xsi:nil=\"true\">"))
1535 (insert "</" xml-tag ">\n")))
1536
1537(defun soap-encode-array-type (xml-tag value type)
1538 "Encode inside XML-TAG the LISP VALUE according to TYPE.
1539Do not call this function directly, use `soap-encode-value'
1540instead."
1541 (unless (vectorp value)
1542 (error "Soap-encode: %s(%s) expects a vector, got: %s"
1543 xml-tag (soap-element-fq-name type) value))
1544 (let* ((element-type (soap-array-type-element-type type))
1545 (array-type (concat (soap-element-fq-name element-type)
1546 "[" (format "%s" (length value)) "]")))
1547 (insert "<" xml-tag
1548 " soapenc:arrayType=\"" array-type "\" "
1549 " xsi:type=\"soapenc:Array\">\n")
1550 (loop for i below (length value)
1551 do (soap-encode-value xml-tag (aref value i) element-type))
1552 (insert "</" xml-tag ">\n")))
1553
1554(progn
1555 (put (aref (make-soap-basic-type) 0)
1556 'soap-encoder 'soap-encode-basic-type)
1557 (put (aref (make-soap-sequence-type) 0)
1558 'soap-encoder 'soap-encode-sequence-type)
1559 (put (aref (make-soap-array-type) 0)
1560 'soap-encoder 'soap-encode-array-type))
1561
1562(defun soap-encode-body (operation parameters wsdl)
1563 "Create the body of a SOAP request for OPERATION in the current buffer.
1564PARAMETERS is a list of parameters supplied to the OPERATION.
1565
1566The OPERATION and PARAMETERS are encoded according to the WSDL
1567document."
1568 (let* ((op (soap-bound-operation-operation operation))
1569 (use (soap-bound-operation-use operation))
1570 (message (cdr (soap-operation-input op)))
1571 (parameter-order (soap-operation-parameter-order op)))
1572
1573 (unless (= (length parameter-order) (length parameters))
1574 (error "Wrong number of parameters for %s: expected %d, got %s"
1575 (soap-element-name op)
1576 (length parameter-order)
1577 (length parameters)))
1578
1579 (insert "<soap:Body>\n")
1580 (when (eq use 'encoded)
1581 (add-to-list 'soap-encoded-namespaces (soap-element-namespace-tag op))
1582 (insert "<" (soap-element-fq-name op) ">\n"))
1583
1584 (let ((param-table (loop for formal in parameter-order
1585 for value in parameters
1586 collect (cons formal value))))
1587 (dolist (part (soap-message-parts message))
1588 (let* ((param-name (car part))
1589 (type (cdr part))
1590 (tag-name (if (eq use 'encoded)
1591 param-name
1592 (soap-element-name type)))
1593 (value (cdr (assoc param-name param-table)))
1594 (start-pos (point)))
1595 (soap-encode-value tag-name value type)
1596 (when (eq use 'literal)
1597 ;; hack: add the xmlns attribute to the tag, the only way
1598 ;; ASP.NET web services recognize the namespace of the
1599 ;; element itself...
1600 (save-excursion
1601 (goto-char start-pos)
1602 (when (re-search-forward " ")
1603 (let* ((ns (soap-element-namespace-tag type))
1604 (namespace (cdr (assoc ns
1605 (soap-wsdl-alias-table wsdl)))))
1606 (when namespace
1607 (insert "xmlns=\"" namespace "\" ")))))))))
1608
1609 (when (eq use 'encoded)
1610 (insert "</" (soap-element-fq-name op) ">\n"))
1611 (insert "</soap:Body>\n")))
1612
1613(defun soap-create-envelope (operation parameters wsdl)
1614 "Create a SOAP request envelope for OPERATION using PARAMETERS.
1615WSDL is the wsdl document used to encode the PARAMETERS."
1616 (with-temp-buffer
1617 (let ((soap-encoded-namespaces '("xsi" "soap" "soapenc"))
1618 (use (soap-bound-operation-use operation)))
1619
1620 ;; Create the request body
1621 (soap-encode-body operation parameters wsdl)
1622
1623 ;; Put the envelope around the body
1624 (goto-char (point-min))
1625 (insert "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soap:Envelope\n")
1626 (when (eq use 'encoded)
1627 (insert " soapenc:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\n"))
1628 (dolist (nstag soap-encoded-namespaces)
1629 (insert " xmlns:" nstag "=\"")
1630 (let ((nsname (cdr (assoc nstag soap-well-known-xmlns))))
1631 (unless nsname
1632 (setq nsname (cdr (assoc nstag (soap-wsdl-alias-table wsdl)))))
1633 (insert nsname)
1634 (insert "\"\n")))
1635 (insert ">\n")
1636 (goto-char (point-max))
1637 (insert "</soap:Envelope>\n"))
1638
1639 (buffer-string)))
1640
1641;;;; invoking soap methods
1642
1643(defcustom soap-debug nil
1644 "When t, enable some debugging facilities."
1645 :type 'boolean
1646 :group 'soap-client)
1647
1648(defun soap-invoke (wsdl service operation-name &rest parameters)
1649 "Invoke a SOAP operation and return the result.
1650
1651WSDL is used for encoding the request and decoding the response.
1652It also contains information about the WEB server address that
1653will service the request.
1654
1655SERVICE is the SOAP service to invoke.
1656
1657OPERATION-NAME is the operation to invoke.
1658
1659PARAMETERS -- the remaining parameters are used as parameters for
1660the SOAP request.
1661
1662NOTE: The SOAP service provider should document the available
1663operations and their parameters for the service. You can also
1664use the `soap-inspect' function to browse the available
1665operations in a WSDL document."
1666 (let ((port (catch 'found
1667 (dolist (p (soap-wsdl-ports wsdl))
1668 (when (equal service (soap-element-name p))
1669 (throw 'found p))))))
1670 (unless port
1671 (error "Unknown SOAP service: %s" service))
1672
1673 (let* ((binding (soap-port-binding port))
1674 (operation (gethash operation-name
1675 (soap-binding-operations binding))))
1676 (unless operation
1677 (error "No operation %s for SOAP service %s" operation-name service))
1678
1679 (let ((url-request-method "POST")
1680 (url-package-name "soap-client.el")
1681 (url-package-version "1.0")
1682 (url-http-version "1.0")
1683 (url-request-data (soap-create-envelope operation parameters wsdl))
1684 (url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
1685 (url-request-coding-system 'utf-8)
1686 (url-http-attempt-keepalives t)
1687 (url-request-extra-headers (list
1688 (cons "SOAPAction"
1689 (soap-bound-operation-soap-action
1690 operation))
1691 (cons "Content-Type"
1692 "text/xml; charset=utf-8"))))
1693 (let ((buffer (url-retrieve-synchronously
1694 (soap-port-service-url port))))
1695 (condition-case err
1696 (with-current-buffer buffer
1697 (declare (special url-http-response-status))
1698 (if (null url-http-response-status)
1699 (error "No HTTP response from server"))
1700 (if (and soap-debug (> url-http-response-status 299))
1701 ;; This is a warning because some SOAP errors come
1702 ;; back with a HTTP response 500 (internal server
1703 ;; error)
1704 (warn "Error in SOAP response: HTTP code %s"
1705 url-http-response-status))
1706 (when (> (buffer-size) 1000000)
1707 (soap-warning
1708 "Received large message: %s bytes"
1709 (buffer-size)))
1710 (let ((mime-part (mm-dissect-buffer t t)))
1711 (unless mime-part
1712 (error "Failed to decode response from server"))
1713 (unless (equal (car (mm-handle-type mime-part)) "text/xml")
1714 (error "Server response is not an XML document"))
1715 (with-temp-buffer
1716 (mm-insert-part mime-part)
1717 (let ((response (car (xml-parse-region
1718 (point-min) (point-max)))))
1719 (prog1
1720 (soap-parse-envelope response operation wsdl)
1721 (kill-buffer buffer)
1722 (mm-destroy-part mime-part))))))
1723 (soap-error
1724 ;; Propagate soap-errors -- they are error replies of the
1725 ;; SOAP protocol and don't indicate a communication
1726 ;; problem or a bug in this code.
1727 (signal (car err) (cdr err)))
1728 (error
1729 (when soap-debug
1730 (pop-to-buffer buffer))
1731 (error (error-message-string err)))))))))
1732
1733(provide 'soap-client)
1734
1735
1736;;; Local Variables:
1737;;; mode: outline-minor
1738;;; outline-regexp: ";;;;+"
1739;;; End:
1740
1741;;; soap-client.el ends here
diff --git a/lisp/net/soap-inspect.el b/lisp/net/soap-inspect.el
new file mode 100644
index 00000000000..7cce9844d76
--- /dev/null
+++ b/lisp/net/soap-inspect.el
@@ -0,0 +1,357 @@
1;;;; soap-inspect.el -- Interactive inspector for soap WSDL structures
2
3;; Copyright (C) 2010-2011 Free Software Foundation, Inc.
4
5;; Author: Alexandru Harsanyi (AlexHarsanyi@gmail.com)
6;; Created: October 2010
7;; Keywords: soap, web-services, comm, hypermedia
8;; Homepage: http://code.google.com/p/emacs-soap-client
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25;;; Commentary:
26;;
27;; This package provides an inspector for a WSDL document loaded with
28;; `soap-load-wsdl' or `soap-load-wsdl-from-url'. To use it, evaluate:
29;;
30;; (soap-inspect *wsdl*)
31;;
32;; This will pop-up the inspector buffer. You can click on ports, operations
33;; and types to explore the structure of the wsdl document.
34;;
35
36
37;;; Code:
38
39(eval-when-compile (require 'cl))
40
41(require 'soap-client)
42
43;;; sample-value
44
45(defun soap-sample-value (type)
46 "Provide a sample value for TYPE, a WSDL type.
47A sample value is a LISP value which soap-client.el will accept
48for encoding it using TYPE when making SOAP requests.
49
50This is a generic function, depending on TYPE a specific function
51will be called."
52 (let ((sample-value (get (aref type 0) 'soap-sample-value)))
53 (if sample-value
54 (funcall sample-value type)
55 (error "Cannot provide sample value for type %s" (aref type 0)))))
56
57(defun soap-sample-value-for-basic-type (type)
58 "Provide a sample value for TYPE which is a basic type.
59This is a specific function which should not be called directly,
60use `soap-sample-value' instead."
61 (case (soap-basic-type-kind type)
62 (string "a string value")
63 (boolean t) ; could be nil as well
64 ((long int) (random 4200))
65 ;; TODO: we need better sample values for more types.
66 (t (format "%s" (soap-basic-type-kind type)))))
67
68(defun soap-sample-value-for-seqence-type (type)
69 "Provide a sample value for TYPE which is a sequence type.
70Values for sequence types are ALISTS of (slot-name . VALUE) for
71each sequence element.
72
73This is a specific function which should not be called directly,
74use `soap-sample-value' instead."
75 (let ((sample-value nil))
76 (dolist (element (soap-sequence-type-elements type))
77 (push (cons (soap-sequence-element-name element)
78 (soap-sample-value (soap-sequence-element-type element)))
79 sample-value))
80 (when (soap-sequence-type-parent type)
81 (setq sample-value
82 (append (soap-sample-value (soap-sequence-type-parent type))
83 sample-value)))
84 sample-value))
85
86(defun soap-sample-value-for-array-type (type)
87 "Provide a sample value for TYPE which is an array type.
88Values for array types are LISP vectors of values which are
89array's element type.
90
91This is a specific function which should not be called directly,
92use `soap-sample-value' instead."
93 (let* ((element-type (soap-array-type-element-type type))
94 (sample1 (soap-sample-value element-type))
95 (sample2 (soap-sample-value element-type)))
96 ;; Our sample value is a vector of two elements, but any number of
97 ;; elements are permissible
98 (vector sample1 sample2 '&etc)))
99
100(defun soap-sample-value-for-message (message)
101 "Provide a sample value for a WSDL MESSAGE.
102This is a specific function which should not be called directly,
103use `soap-sample-value' instead."
104 ;; NOTE: parameter order is not considered.
105 (let (sample-value)
106 (dolist (part (soap-message-parts message))
107 (push (cons (car part)
108 (soap-sample-value (cdr part)))
109 sample-value))
110 (nreverse sample-value)))
111
112(progn
113 ;; Install soap-sample-value methods for our types
114 (put (aref (make-soap-basic-type) 0) 'soap-sample-value
115 'soap-sample-value-for-basic-type)
116
117 (put (aref (make-soap-sequence-type) 0) 'soap-sample-value
118 'soap-sample-value-for-seqence-type)
119
120 (put (aref (make-soap-array-type) 0) 'soap-sample-value
121 'soap-sample-value-for-array-type)
122
123 (put (aref (make-soap-message) 0) 'soap-sample-value
124 'soap-sample-value-for-message) )
125
126
127
128;;; soap-inspect
129
130(defvar soap-inspect-previous-items nil
131 "A stack of previously inspected items in the *soap-inspect* buffer.
132Used to implement the BACK button.")
133
134(defvar soap-inspect-current-item nil
135 "The current item being inspected in the *soap-inspect* buffer.")
136
137(progn
138 (make-variable-buffer-local 'soap-inspect-previous-items)
139 (make-variable-buffer-local 'soap-inspect-current-item))
140
141(defun soap-inspect (element)
142 "Inspect a SOAP ELEMENT in the *soap-inspect* buffer.
143The buffer is populated with information about ELEMENT with links
144to its sub elements. If ELEMENT is the WSDL document itself, the
145entire WSDL can be inspected."
146 (let ((inspect (get (aref element 0) 'soap-inspect)))
147 (unless inspect
148 (error "Soap-inspect: no inspector for element"))
149
150 (with-current-buffer (get-buffer-create "*soap-inspect*")
151 (setq buffer-read-only t)
152 (let ((inhibit-read-only t))
153 (erase-buffer)
154
155 (when soap-inspect-current-item
156 (push soap-inspect-current-item
157 soap-inspect-previous-items))
158 (setq soap-inspect-current-item element)
159
160 (funcall inspect element)
161
162 (unless (null soap-inspect-previous-items)
163 (insert "\n\n")
164 (insert-text-button
165 "[back]"
166 'type 'soap-client-describe-back-link
167 'item element)
168 (insert "\n"))
169 (goto-char (point-min))
170 (pop-to-buffer (current-buffer))))))
171
172
173(define-button-type 'soap-client-describe-link
174 'face 'italic
175 'help-echo "mouse-2, RET: describe item"
176 'follow-link t
177 'action (lambda (button)
178 (let ((item (button-get button 'item)))
179 (soap-inspect item)))
180 'skip t)
181
182(define-button-type 'soap-client-describe-back-link
183 'face 'italic
184 'help-echo "mouse-2, RET: browse the previous item"
185 'follow-link t
186 'action (lambda (button)
187 (let ((item (pop soap-inspect-previous-items)))
188 (when item
189 (setq soap-inspect-current-item nil)
190 (soap-inspect item))))
191 'skip t)
192
193(defun soap-insert-describe-button (element)
194 "Insert a button to inspect ELEMENT when pressed."
195 (insert-text-button
196 (soap-element-fq-name element)
197 'type 'soap-client-describe-link
198 'item element))
199
200(defun soap-inspect-basic-type (basic-type)
201 "Insert information about BASIC-TYPE into the current buffer."
202 (insert "Basic type: " (soap-element-fq-name basic-type))
203 (insert "\nSample value\n")
204 (pp (soap-sample-value basic-type) (current-buffer)))
205
206(defun soap-inspect-sequence-type (sequence)
207 "Insert information about SEQUENCE into the current buffer."
208 (insert "Sequence type: " (soap-element-fq-name sequence) "\n")
209 (when (soap-sequence-type-parent sequence)
210 (insert "Parent: ")
211 (soap-insert-describe-button
212 (soap-sequence-type-parent sequence))
213 (insert "\n"))
214 (insert "Elements: \n")
215 (dolist (element (soap-sequence-type-elements sequence))
216 (insert "\t" (symbol-name (soap-sequence-element-name element))
217 "\t")
218 (soap-insert-describe-button
219 (soap-sequence-element-type element))
220 (when (soap-sequence-element-multiple? element)
221 (insert " multiple"))
222 (when (soap-sequence-element-nillable? element)
223 (insert " optional"))
224 (insert "\n"))
225 (insert "Sample value:\n")
226 (pp (soap-sample-value sequence) (current-buffer)))
227
228(defun soap-inspect-array-type (array)
229 "Insert information about the ARRAY into the current buffer."
230 (insert "Array name: " (soap-element-fq-name array) "\n")
231 (insert "Element type: ")
232 (soap-insert-describe-button
233 (soap-array-type-element-type array))
234 (insert "\nSample value:\n")
235 (pp (soap-sample-value array) (current-buffer)))
236
237(defun soap-inspect-message (message)
238 "Insert information about MESSAGE into the current buffer."
239 (insert "Message name: " (soap-element-fq-name message) "\n")
240 (insert "Parts:\n")
241 (dolist (part (soap-message-parts message))
242 (insert "\t" (symbol-name (car part))
243 " type: ")
244 (soap-insert-describe-button (cdr part))
245 (insert "\n")))
246
247(defun soap-inspect-operation (operation)
248 "Insert information about OPERATION into the current buffer."
249 (insert "Operation name: " (soap-element-fq-name operation) "\n")
250 (let ((input (soap-operation-input operation)))
251 (insert "\tInput: " (symbol-name (car input)) " (" )
252 (soap-insert-describe-button (cdr input))
253 (insert ")\n"))
254 (let ((output (soap-operation-output operation)))
255 (insert "\tOutput: " (symbol-name (car output)) " (")
256 (soap-insert-describe-button (cdr output))
257 (insert ")\n"))
258
259 (insert "\n\nSample invocation:\n")
260 (let ((sample-message-value
261 (soap-sample-value (cdr (soap-operation-input operation))))
262 (funcall (list 'soap-invoke '*WSDL* "SomeService" (soap-element-name operation))))
263 (let ((sample-invocation
264 (append funcall (mapcar 'cdr sample-message-value))))
265 (pp sample-invocation (current-buffer)))))
266
267(defun soap-inspect-port-type (port-type)
268 "Insert information about PORT-TYPE into the current buffer."
269 (insert "Port-type name: " (soap-element-fq-name port-type) "\n")
270 (insert "Operations:\n")
271 (loop for o being the hash-values of
272 (soap-namespace-elements (soap-port-type-operations port-type))
273 do (progn
274 (insert "\t")
275 (soap-insert-describe-button (car o)))))
276
277(defun soap-inspect-binding (binding)
278 "Insert information about BINDING into the current buffer."
279 (insert "Binding: " (soap-element-fq-name binding) "\n")
280 (insert "\n")
281 (insert "Bound operations:\n")
282 (let* ((ophash (soap-binding-operations binding))
283 (operations (loop for o being the hash-keys of ophash
284 collect o))
285 op-name-width)
286
287 (setq operations (sort operations 'string<))
288
289 (setq op-name-width (loop for o in operations maximizing (length o)))
290
291 (dolist (op operations)
292 (let* ((bound-op (gethash op ophash))
293 (soap-action (soap-bound-operation-soap-action bound-op))
294 (use (soap-bound-operation-use bound-op)))
295 (unless soap-action
296 (setq soap-action ""))
297 (insert "\t")
298 (soap-insert-describe-button (soap-bound-operation-operation bound-op))
299 (when (or use (not (equal soap-action "")))
300 (insert (make-string (- op-name-width (length op)) ?\s))
301 (insert " (")
302 (insert soap-action)
303 (when use
304 (insert " " (symbol-name use)))
305 (insert ")"))
306 (insert "\n")))))
307
308(defun soap-inspect-port (port)
309 "Insert information about PORT into the current buffer."
310 (insert "Port name: " (soap-element-name port) "\n"
311 "Service URL: " (soap-port-service-url port) "\n"
312 "Binding: ")
313 (soap-insert-describe-button (soap-port-binding port)))
314
315(defun soap-inspect-wsdl (wsdl)
316 "Insert information about WSDL into the current buffer."
317 (insert "WSDL Origin: " (soap-wsdl-origin wsdl) "\n")
318 (insert "Ports:")
319 (dolist (p (soap-wsdl-ports wsdl))
320 (insert "\n--------------------\n")
321 ;; (soap-insert-describe-button p)
322 (soap-inspect-port p))
323 (insert "\n--------------------\nNamespace alias table:\n")
324 (dolist (a (soap-wsdl-alias-table wsdl))
325 (insert "\t" (car a) " => " (cdr a) "\n")))
326
327(progn
328 ;; Install the soap-inspect methods for our types
329
330 (put (aref (make-soap-basic-type) 0) 'soap-inspect
331 'soap-inspect-basic-type)
332
333 (put (aref (make-soap-sequence-type) 0) 'soap-inspect
334 'soap-inspect-sequence-type)
335
336 (put (aref (make-soap-array-type) 0) 'soap-inspect
337 'soap-inspect-array-type)
338
339 (put (aref (make-soap-message) 0) 'soap-inspect
340 'soap-inspect-message)
341 (put (aref (make-soap-operation) 0) 'soap-inspect
342 'soap-inspect-operation)
343
344 (put (aref (make-soap-port-type) 0) 'soap-inspect
345 'soap-inspect-port-type)
346
347 (put (aref (make-soap-binding) 0) 'soap-inspect
348 'soap-inspect-binding)
349
350 (put (aref (make-soap-port) 0) 'soap-inspect
351 'soap-inspect-port)
352
353 (put (aref (make-soap-wsdl) 0) 'soap-inspect
354 'soap-inspect-wsdl))
355
356(provide 'soap-inspect)
357;;; soap-inspect.el ends here
diff --git a/lisp/play/doctor.el b/lisp/play/doctor.el
index 5b3b4aba0fe..c60472e9386 100644
--- a/lisp/play/doctor.el
+++ b/lisp/play/doctor.el
@@ -141,7 +141,7 @@ reads the sentence before point, and prints the Doctor's answer."
141 (turn-on-auto-fill) 141 (turn-on-auto-fill)
142 (doctor-type '(i am the psychotherapist \. 142 (doctor-type '(i am the psychotherapist \.
143 (doc$ doctor--please) (doc$ doctor--describe) your (doc$ doctor--problems) \. 143 (doc$ doctor--please) (doc$ doctor--describe) your (doc$ doctor--problems) \.
144 each time you are finished talking, type \R\E\T twice \.)) 144 each time you are finished talking\, type \R\E\T twice \.))
145 (insert "\n")) 145 (insert "\n"))
146 146
147(defun make-doctor-variables () 147(defun make-doctor-variables ()
diff --git a/lisp/progmodes/prolog.el b/lisp/progmodes/prolog.el
index fd79cfd2399..86553f9496e 100644
--- a/lisp/progmodes/prolog.el
+++ b/lisp/progmodes/prolog.el
@@ -5,8 +5,9 @@
5 5
6;; Authors: Emil Åström <emil_astrom(at)hotmail(dot)com> 6;; Authors: Emil Åström <emil_astrom(at)hotmail(dot)com>
7;; Milan Zamazal <pdm(at)freesoft(dot)cz> 7;; Milan Zamazal <pdm(at)freesoft(dot)cz>
8;; Stefan Bruda <stefan(at)bruda(dot)ca> (current maintainer) 8;; Stefan Bruda <stefan(at)bruda(dot)ca>
9;; * See below for more details 9;; * See below for more details
10;; Maintainer: Stefan Bruda <stefan(at)bruda(dot)ca>
10;; Keywords: prolog major mode sicstus swi mercury 11;; Keywords: prolog major mode sicstus swi mercury
11 12
12(defvar prolog-mode-version "1.22" 13(defvar prolog-mode-version "1.22"
diff --git a/lisp/shell.el b/lisp/shell.el
index fcffc2317d5..ea89ce765c3 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -151,12 +151,14 @@ This is a fine thing to set in your `.emacs' file."
151 :type '(repeat (string :tag "Suffix")) 151 :type '(repeat (string :tag "Suffix"))
152 :group 'shell) 152 :group 'shell)
153 153
154(defvar shell-delimiter-argument-list '(?\| ?& ?< ?> ?\( ?\) ?\;) 154(defcustom shell-delimiter-argument-list nil ; '(?\| ?& ?< ?> ?\( ?\) ?\;)
155 "List of characters to recognize as separate arguments. 155 "List of characters to recognize as separate arguments.
156This variable is used to initialize `comint-delimiter-argument-list' in the 156This variable is used to initialize `comint-delimiter-argument-list' in the
157shell buffer. The value may depend on the operating system or shell. 157shell buffer. The value may depend on the operating system or shell."
158 158 :type '(choice (const nil)
159This is a fine thing to set in your `.emacs' file.") 159 (repeat :tag "List of characters" character))
160 :version "24.1" ; changed to nil (bug#8027)
161 :group 'shell)
160 162
161(defvar shell-file-name-chars 163(defvar shell-file-name-chars
162 (if (memq system-type '(ms-dos windows-nt cygwin)) 164 (if (memq system-type '(ms-dos windows-nt cygwin))
diff --git a/lisp/simple.el b/lisp/simple.el
index 4d2a0e69836..531c9212e34 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -778,7 +778,7 @@ If N is negative, delete newlines as well."
778 (n (abs n))) 778 (n (abs n)))
779 (skip-chars-backward skip-characters) 779 (skip-chars-backward skip-characters)
780 (constrain-to-field nil orig-pos) 780 (constrain-to-field nil orig-pos)
781 (dotimes (i (or n 1)) 781 (dotimes (i n)
782 (if (= (following-char) ?\s) 782 (if (= (following-char) ?\s)
783 (forward-char 1) 783 (forward-char 1)
784 (insert ?\s))) 784 (insert ?\s)))
diff --git a/lisp/speedbar.el b/lisp/speedbar.el
index b84afd797d1..dad2a4c82ac 100644
--- a/lisp/speedbar.el
+++ b/lisp/speedbar.el
@@ -614,8 +614,11 @@ state data."
614 :group 'speedbar 614 :group 'speedbar
615 :type 'hook) 615 :type 'hook)
616 616
617(defvar speedbar-ignored-modes '(fundamental-mode) 617(defcustom speedbar-ignored-modes '(fundamental-mode)
618 "*List of major modes which speedbar will not switch directories for.") 618 "List of major modes which speedbar will not switch directories for."
619 :group 'speedbar
620 :type '(choice (const nil)
621 (repeat :tag "List of modes" (symbol :tag "Major mode"))))
619 622
620(defun speedbar-extension-list-to-regex (extlist) 623(defun speedbar-extension-list-to-regex (extlist)
621 "Takes EXTLIST, a list of extensions and transforms it into regexp. 624 "Takes EXTLIST, a list of extensions and transforms it into regexp.
@@ -669,7 +672,7 @@ directories here; see `vc-directory-exclusion-list'."
669 :group 'speedbar 672 :group 'speedbar
670 :type 'string) 673 :type 'string)
671 674
672(defvar speedbar-file-unshown-regexp 675(defcustom speedbar-file-unshown-regexp
673 (let ((nstr "") (noext completion-ignored-extensions)) 676 (let ((nstr "") (noext completion-ignored-extensions))
674 (while noext 677 (while noext
675 (setq nstr (concat nstr (regexp-quote (car noext)) "\\'" 678 (setq nstr (concat nstr (regexp-quote (car noext)) "\\'"
@@ -677,8 +680,10 @@ directories here; see `vc-directory-exclusion-list'."
677 noext (cdr noext))) 680 noext (cdr noext)))
678 ;; backup refdir lockfile 681 ;; backup refdir lockfile
679 (concat nstr "\\|#[^#]+#$\\|\\.\\.?\\'\\|\\.#")) 682 (concat nstr "\\|#[^#]+#$\\|\\.\\.?\\'\\|\\.#"))
680 "*Regexp matching files we don't want displayed in a speedbar buffer. 683 "Regexp matching files we don't want displayed in a speedbar buffer.
681It is generated from the variable `completion-ignored-extensions'.") 684It is generated from the variable `completion-ignored-extensions'."
685 :group 'speedbar
686 :type 'string)
682 687
683(defvar speedbar-file-regexp nil 688(defvar speedbar-file-regexp nil
684 "Regular expression matching files we know how to expand. 689 "Regular expression matching files we know how to expand.
@@ -755,14 +760,17 @@ DIRECTORY-EXPRESSION to `speedbar-ignored-directory-expressions'."
755 speedbar-ignored-directory-regexp (speedbar-extension-list-to-regex 760 speedbar-ignored-directory-regexp (speedbar-extension-list-to-regex
756 speedbar-ignored-directory-expressions))) 761 speedbar-ignored-directory-expressions)))
757 762
758(defvar speedbar-update-flag dframe-have-timer-flag 763(defcustom speedbar-update-flag dframe-have-timer-flag
759 "*Non-nil means to automatically update the display. 764 "Non-nil means to automatically update the display.
760When this is nil then speedbar will not follow the attached frame's directory. 765When this is nil then speedbar will not follow the attached frame's directory.
761When speedbar is active, use: 766If you want to change this while speedbar is active, either use
762 767\\[customize] or call \\<speedbar-key-map> `\\[speedbar-toggle-updates]'."
763\\<speedbar-key-map> `\\[speedbar-toggle-updates]' 768 :group 'speedbar
764 769 :initialize 'custom-initialize-default
765to toggle this value.") 770 :set (lambda (sym val)
771 (set sym val)
772 (speedbar-toggle-updates))
773 :type 'boolean)
766 774
767(defvar speedbar-update-flag-disable nil 775(defvar speedbar-update-flag-disable nil
768 "Permanently disable changing of the update flag.") 776 "Permanently disable changing of the update flag.")
@@ -3643,17 +3651,20 @@ to be at the beginning of a line in the etags buffer.
3643 3651
3644This variable is ignored if `speedbar-use-imenu-flag' is non-nil.") 3652This variable is ignored if `speedbar-use-imenu-flag' is non-nil.")
3645 3653
3646(defvar speedbar-fetch-etags-command "etags" 3654(defcustom speedbar-fetch-etags-command "etags"
3647 "*Command used to create an etags file. 3655 "Command used to create an etags file.
3648 3656This variable is ignored if `speedbar-use-imenu-flag' is t."
3649This variable is ignored if `speedbar-use-imenu-flag' is t.") 3657 :group 'speedbar
3658 :type 'string)
3650 3659
3651(defvar speedbar-fetch-etags-arguments '("-D" "-I" "-o" "-") 3660(defcustom speedbar-fetch-etags-arguments '("-D" "-I" "-o" "-")
3652 "*List of arguments to use with `speedbar-fetch-etags-command'. 3661 "List of arguments to use with `speedbar-fetch-etags-command'.
3653This creates an etags output buffer. Use `speedbar-toggle-etags' to 3662This creates an etags output buffer. Use `speedbar-toggle-etags' to
3654modify this list conveniently. 3663modify this list conveniently.
3655 3664This variable is ignored if `speedbar-use-imenu-flag' is t."
3656This variable is ignored if `speedbar-use-imenu-flag' is t.") 3665 :group 'speedbar
3666 :type '(choice (const nil)
3667 (repeat :tag "List of arguments" string)))
3657 3668
3658(defun speedbar-toggle-etags (flag) 3669(defun speedbar-toggle-etags (flag)
3659 "Toggle FLAG in `speedbar-fetch-etags-arguments'. 3670 "Toggle FLAG in `speedbar-fetch-etags-arguments'.
diff --git a/lisp/term.el b/lisp/term.el
index ea419234e0f..df95ca830ab 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -762,11 +762,13 @@ Buffer local variable.")
762 "magenta3" "cyan3" "white"]) 762 "magenta3" "cyan3" "white"])
763 763
764;; Inspiration came from comint.el -mm 764;; Inspiration came from comint.el -mm
765(defvar term-buffer-maximum-size 2048 765(defcustom term-buffer-maximum-size 2048
766 "*The maximum size in lines for term buffers. 766 "The maximum size in lines for term buffers.
767Term buffers are truncated from the top to be no greater than this number. 767Term buffers are truncated from the top to be no greater than this number.
768Notice that a setting of 0 means \"don't truncate anything\". This variable 768Notice that a setting of 0 means \"don't truncate anything\". This variable
769is buffer-local.") 769is buffer-local."
770 :group 'term
771 :type 'integer)
770 772
771(when (featurep 'xemacs) 773(when (featurep 'xemacs)
772 (defvar term-terminal-menu 774 (defvar term-terminal-menu
@@ -2209,9 +2211,11 @@ Security bug: your string can still be temporarily recovered with
2209 2211
2210;;; Low-level process communication 2212;;; Low-level process communication
2211 2213
2212(defvar term-input-chunk-size 512 2214(defcustom term-input-chunk-size 512
2213 "*Long inputs send to term processes are broken up into chunks of this size. 2215 "Long inputs send to term processes are broken up into chunks of this size.
2214If your process is choking on big inputs, try lowering the value.") 2216If your process is choking on big inputs, try lowering the value."
2217 :group 'term
2218 :type 'integer)
2215 2219
2216(defun term-send-string (proc str) 2220(defun term-send-string (proc str)
2217 "Send to PROC the contents of STR as input. 2221 "Send to PROC the contents of STR as input.
@@ -3909,27 +3913,38 @@ This is a good place to put keybindings.")
3909;; Commands like this are fine things to put in load hooks if you 3913;; Commands like this are fine things to put in load hooks if you
3910;; want them present in specific modes. 3914;; want them present in specific modes.
3911 3915
3912(defvar term-completion-autolist nil 3916(defcustom term-completion-autolist nil
3913 "*If non-nil, automatically list possibilities on partial completion. 3917 "If non-nil, automatically list possibilities on partial completion.
3914This mirrors the optional behavior of tcsh.") 3918This mirrors the optional behavior of tcsh."
3919 :group 'term
3920 :type 'boolean)
3915 3921
3916(defvar term-completion-addsuffix t 3922(defcustom term-completion-addsuffix t
3917 "*If non-nil, add a `/' to completed directories, ` ' to file names. 3923 "If non-nil, add a `/' to completed directories, ` ' to file names.
3918If a cons pair, it should be of the form (DIRSUFFIX . FILESUFFIX) where 3924If a cons pair, it should be of the form (DIRSUFFIX . FILESUFFIX) where
3919DIRSUFFIX and FILESUFFIX are strings added on unambiguous or exact 3925DIRSUFFIX and FILESUFFIX are strings added on unambiguous or exact
3920completion. This mirrors the optional behavior of tcsh.") 3926completion. This mirrors the optional behavior of tcsh."
3927 :group 'term
3928 :type '(choice (const :tag "No suffix" nil)
3929 (cons (string :tag "dirsuffix") (string :tag "filesuffix"))
3930 (other :tag "Suffix" t)))
3921 3931
3922(defvar term-completion-recexact nil 3932(defcustom term-completion-recexact nil
3923 "*If non-nil, use shortest completion if characters cannot be added. 3933 "If non-nil, use shortest completion if characters cannot be added.
3924This mirrors the optional behavior of tcsh. 3934This mirrors the optional behavior of tcsh.
3925 3935
3926A non-nil value is useful if `term-completion-autolist' is non-nil too.") 3936A non-nil value is useful if `term-completion-autolist' is non-nil too."
3937 :group 'term
3938 :type 'boolean)
3927 3939
3928(defvar term-completion-fignore nil 3940(defcustom term-completion-fignore nil
3929 "*List of suffixes to be disregarded during file completion. 3941 "List of suffixes to be disregarded during file completion.
3930This mirrors the optional behavior of bash and tcsh. 3942This mirrors the optional behavior of bash and tcsh.
3931 3943
3932Note that this applies to `term-dynamic-complete-filename' only.") 3944Note that this applies to `term-dynamic-complete-filename' only."
3945 :group 'term
3946 :type '(choice (const nil)
3947 (repeat :tag "List of suffixes" string)))
3933 3948
3934(defvar term-file-name-prefix "" 3949(defvar term-file-name-prefix ""
3935 "Prefix prepended to absolute file names taken from process input. 3950 "Prefix prepended to absolute file names taken from process input.
diff --git a/lisp/term/sup-mouse.el b/lisp/term/sup-mouse.el
index a8b78bb3e38..6d77241008c 100644
--- a/lisp/term/sup-mouse.el
+++ b/lisp/term/sup-mouse.el
@@ -30,8 +30,11 @@
30 30
31;;; User customization option: 31;;; User customization option:
32 32
33(defvar sup-mouse-fast-select-window nil 33(defcustom sup-mouse-fast-select-window nil
34 "*Non-nil for mouse hits to select new window, then execute; else just select.") 34 "Non-nil means mouse hits select new window, then execute.
35Otherwise just select."
36 :type 'boolean
37 :group 'mouse)
35 38
36(defconst mouse-left 0) 39(defconst mouse-left 0)
37(defconst mouse-center 1) 40(defconst mouse-center 1)
diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el
index 1ec80d5c277..e3c42626a3f 100644
--- a/lisp/term/x-win.el
+++ b/lisp/term/x-win.el
@@ -1167,20 +1167,28 @@ pasted text.")
1167 :group 'killing 1167 :group 'killing
1168 :version "24.1") 1168 :version "24.1")
1169 1169
1170(defvar x-select-request-type nil 1170(defcustom x-select-request-type nil
1171 "*Data type request for X selection. 1171 "Data type request for X selection.
1172The value is one of the following data types, a list of them, or nil: 1172The value is one of the following data types, a list of them, or nil:
1173 `COMPOUND_TEXT', `UTF8_STRING', `STRING', `TEXT' 1173 `COMPOUND_TEXT', `UTF8_STRING', `STRING', `TEXT'
1174 1174
1175If the value is one of the above symbols, try only the specified 1175If the value is one of the above symbols, try only the specified type.
1176type.
1177 1176
1178If the value is a list of them, try each of them in the specified 1177If the value is a list of them, try each of them in the specified
1179order until succeed. 1178order until succeed.
1180 1179
1181The value nil is the same as this list: 1180The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING)."
1182 \(UTF8_STRING COMPOUND_TEXT STRING) 1181 :type '(choice (const :tag "Default" nil)
1183") 1182 (const COMPOUND_TEXT)
1183 (const UTF8_STRING)
1184 (const STRING)
1185 (const TEXT)
1186 (set :tag "List of values"
1187 (const COMPOUND_TEXT)
1188 (const UTF8_STRING)
1189 (const STRING)
1190 (const TEXT)))
1191 :group 'killing)
1184 1192
1185;; Get a selection value of type TYPE by calling x-get-selection with 1193;; Get a selection value of type TYPE by calling x-get-selection with
1186;; an appropriate DATA-TYPE argument decided by `x-select-request-type'. 1194;; an appropriate DATA-TYPE argument decided by `x-select-request-type'.
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 815bdbfc5bf..02743847800 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -2614,9 +2614,6 @@ log entries should be gathered."
2614 (when index 2614 (when index
2615 (substring rev 0 index)))) 2615 (substring rev 0 index))))
2616 2616
2617(define-obsolete-function-alias
2618 'vc-default-previous-version 'vc-default-previous-revision "23.1")
2619
2620(defun vc-default-responsible-p (backend file) 2617(defun vc-default-responsible-p (backend file)
2621 "Indicate whether BACKEND is reponsible for FILE. 2618 "Indicate whether BACKEND is reponsible for FILE.
2622The default is to return nil always." 2619The default is to return nil always."