comparison lisp/dired/gmhist.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;;; gmhist.el - Provide generic minibuffer history for commands
2
3 (defconst gmhist-version (substring "!Revision: 4.27 !" 11 -2)
4 "Id: gmhist.el,v 4.27 1992/04/20 17:17:47 sk RelBeta
5 Report bugs to Sebastian Kremer <sk@thp.uni-koeln.de>.")
6
7 ;; Copyright (C) 1990 by Sebastian Kremer <sk@thp.uni-koeln.de>
8
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 1, or (at your option)
12 ;; any later version.
13 ;;
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18 ;;
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program; if not, write to the Free Software
21 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 ;; LISPDIR ENTRY for the Elisp Archive ===============================
24 ;; LCD Archive Entry:
25 ;; gmhist|Sebastian Kremer|sk@thp.uni-koeln.de
26 ;; |Generic minibuffer history package.
27 ;; |Date: 1992/04/20 17:17:47 |Revision: 4.27 |
28
29 ;; INSTALLATION ======================================================
30 ;;
31 ;; Put this file into your load-path and the following in your
32 ;; ~/.emacs:
33 ;;
34 ;; (autoload 'read-with-history-in "gmhist")
35 ;; (autoload 'read-file-name-with-history-in "gmhist")
36 ;; (autoload 'completing-read-with-history-in "gmhist")
37 ;; (autoload 'gmhist-make-magic "gmhist" nil t)
38
39 ;; USAGE =============================================================
40 ;;
41 ;; - as an Elisp programmer: use functions read-with-history-in,
42 ;; completing-read-with-history-in, read-file-name-with-history-in or
43 ;; gmhist-interactive inside the interactive clause of your functions
44 ;; instead of a string specification. See the examples at the end of
45 ;; the file.
46 ;;
47 ;; - as an Emacs user: To provide `simple' functions with history,
48 ;; just type M-x gmhist-make-magic and enter the name of the
49 ;; function, e.g., `eval-expression'. This function's arguments
50 ;; are then remembered across calls and are available by typing
51 ;; M-p to the minibuffer prompt of the function. More history
52 ;; commands are mentioned in the documentation of variable
53 ;; gmhist-map.
54 ;;
55 ;; Type M-x gmhist-remove-magic to restore the function's old
56 ;; interactive behaviour.
57 ;;
58 ;; `Simple' functions are those that prompt for strings, file
59 ;; names or lisp objects and perhaps use prefix args and the
60 ;; region. See the file gmhist-app.el for examples with simple
61 ;; and other functions.
62
63 ;; I'd like to thank Jamie Zawinski, Piet van Oostrum and Mike
64 ;; Williams for very helpful feedback and ideas.
65
66
67 (provide 'gmhist)
68
69 ;; Emacs 19 has s-expr interactive's on some functions (sometimes to
70 ;; emulate functionality gmhist would give). So we sometimes have to
71 ;; test this to avoid letting gmhist-make-magic bombing on non-string
72 ;; interactive specifications:
73 ;; XEmacs fix:
74 (defvar gmhist-emacs-19-p (not (equal (substring emacs-version 0 2) "18")))
75
76 (defvar gmhist-default-format "[%s] " ; saves screen space, too
77 "Format used by gmhist to indicate the presence of a default value.
78 Set this to \"(default %s) \" to get the standard format.")
79
80 (defvar gmhist-search-history nil "History of history searches.")
81
82 (defun read-with-history-in (GMHIST-SYMBOL rwhi-prompt &optional
83 GMHIST-INITIAL GMHIST-READ)
84 ;; We have to be careful about dynamical scoping here so as not to
85 ;; shadow other lisp code that depends on fluid vars like `prompt
86 ;; (notorious in minibuffer code, e.g. electric-replace).
87 ;; That's why our own fluid vars have upper-case names starting with
88 ;; GMHIST- and why `rwhi-prompt' instead of `prompt' is used as
89 ;; formal argument. Similar below.
90 "\
91 Read a string, maintaining minibuffer history across calls in GMHIST-SYMBOL,
92 prompting with PROMPT, with optional GMHIST-INITIAL as initial contents.
93 If optional fourth arg GMHIST-READ is non-nil, then interpret the
94 result as a lisp object and return that object.
95 See variable gmhist-map for history commands available during edit.
96 Example:
97 (defun foo-command (cmd)
98 (interactive (list (read-with-history-in 'foo-history \"Foo: \" )))
99 (message \"Fooing %s...\" cmd))
100
101 See function gmhist-make-magic on how to give an existing function
102 history.
103
104 These properties (see function put) of GMHIST-SYMBOL are supported:
105
106 cursor-end Put cursor at end of a newly retrieved history line.
107 cursor-pos A regexp to put the cursor on.
108 keep-dups If t, duplicate commands are remembered, too.
109 initial-hist Initial value of the history list.
110 hist-ignore Regexp of commands that are not to be added to the history.
111 backup If t, backup in the history list (as if user had typed
112 M-p as first thing). Can also be an integer to backup
113 more than one history item.
114 default An empty string as input will default to the last
115 command (whether the last command was added to the
116 history or not). The default is stored in this
117 property, thus its initial value is the first default.
118 dangerous Commands matching this regexp will never be the default.
119 no-default If you don't want defaults at all, set this to t.
120
121 Use the following only if you know what you are doing:
122
123 hist-function Name of a function to call instead of doing normal
124 history processing. read-with-history-in becomes
125 effectively an alias for this function.
126
127 These will be flushed soon (use let-binding minibuffer-completion-table
128 etc. instead):
129
130 hist-map Minibuffer key map to use instead of gmhist-map.
131 completion-table
132 completion-predicate
133 Used in completion on history strings, when the hist-map
134 property has gmhist-completion-map as value.
135 The special value `t' for the table means to use the
136 current history list.
137 Thus, to get completion on history items just do:
138 (put 'foo-history 'hist-map gmhist-completion-map)
139 (put 'foo-history 'completion-table t)
140
141 Hooks:
142 gmhist-after-insert-hook is run after a history item is
143 inserted into the minibuffer.
144 gmhist-load-hook is run after this package is loaded.
145 gmhist-hook is run as first thing inside read-with-history-in.
146 gmhist-before-move-hook is run before history motion takes place.
147 Function gmhist-remember-zero is a candidate for that hook.
148 "
149 ;; We don't use property names prefixed with 'ghmist-' because the
150 ;; caller has freedom to use anything for GMHIST-SYMBOL.
151 ;; The history list is never truncated, but I don't think this will
152 ;; cause problems. All histories together have at most a few k.
153 ;; On the other hand, some people run an Emacs session for weeks.
154 ;; Could use gmhist-hook to truncate the current history list.
155 ;; You can use 'initial-hist to save (part of) the history in a file
156 ;; and provide it at next startup. [Is there an exit-emacs-hook?]
157 ;; You can use 'hist-function to implement a completely different
158 ;; history mechanism, e.g., a ring instead of a list, without having
159 ;; to modify existing gmhist applications.
160 (run-hooks 'gmhist-hook)
161 (let ((hist-function (get GMHIST-SYMBOL 'hist-function)))
162 (if (fboundp hist-function) ; hist-function must be a symbol
163 (funcall hist-function ; not lambda
164 GMHIST-SYMBOL rwhi-prompt GMHIST-INITIAL GMHIST-READ)
165 (or (boundp GMHIST-SYMBOL) ; history list defaults to nil
166 (set GMHIST-SYMBOL (get GMHIST-SYMBOL 'initial-hist)))
167 ;; else do the usual history processing simply using lists:
168 (let* ((history (symbol-value GMHIST-SYMBOL))
169 (minibuffer-completion-table (let ((table
170 (get GMHIST-SYMBOL
171 'completion-table)))
172 (if (eq t table)
173 (mapcar (function list)
174 history)
175 table)))
176 (minibuffer-completion-predicate (get GMHIST-SYMBOL
177 'completion-predicate))
178 (minibuffer-history-symbol GMHIST-SYMBOL))
179 (gmhist-new-read-from-minibuffer rwhi-prompt
180 GMHIST-INITIAL
181 (or (get GMHIST-SYMBOL 'hist-map)
182 gmhist-map)
183 GMHIST-READ)))))
184
185 (defun completing-read-with-history-in (crwhi-hist-sym &rest args)
186 "Like completing-read, but with additional first arg HISTORY-SYMBOL."
187 (let ((minibuffer-history-symbol crwhi-hist-sym))
188 (apply 'gmhist-completing-read args)))
189
190 (defun gmhist-completing-read (crwhi-prompt table
191 &optional predicate
192 mustmatch initial)
193 "Like completing-read, but see minibuffer-history-symbol."
194 (let ((minibuffer-completion-confirm (if (eq mustmatch t) nil t))
195 (minibuffer-completion-table table)
196 (minibuffer-completion-predicate predicate))
197 (gmhist-new-read-from-minibuffer crwhi-prompt
198 initial
199 (gmhist-lookup-keymap
200 (if mustmatch
201 gmhist-must-match-map
202 gmhist-completion-map)))))
203
204
205 (defun read-file-name-with-history-in (crwhi-hist-sym &rest args)
206 "Like read-file-name, but with additional first arg HISTORY-SYMBOL."
207 (let ((file-history-symbol crwhi-hist-sym))
208 (apply 'gmhist-read-file-name args)))
209
210 (defvar file-history-symbol 'file-history
211 "*If non-nil, it is the name (a symbol) of a variable on which to cons
212 filenames entered in the minibuffer.
213 You may let-bind this to another symbol around calls to read-file-name.")
214
215 (defun gmhist-read-file-name
216 (grfn-prompt &optional dir default mustmatch initial)
217 "Args: PROMPT &optional DIR DEFAULT MUSTMATCH INITIAL.
218 Read file name, maintaining history in file-history-symbol, prompting
219 with PROMPT, with optional INITIAL input and completing in directory DIR.
220 Value is not expanded! You must call expand-file-name yourself.
221 Default name to arg DEFAULT if user enters a null string (or, if
222 INITIAL was given, leaves it unchanged).
223 MUSTMATCH non-nil means require existing file's name.
224 Non-nil and non-t means also require confirmation after completion.
225 DIR defaults to current buffer's default-directory.
226
227 This function differs from read-file-name in providing a history of
228 filenames bound to file-history-symbol and (for pre-Emacs 19) in
229 providing an argument INITIAL not present in Emacs 18's read-file-name."
230 (setq dir (or dir default-directory)
231 default (or default buffer-file-name))
232 (if file-history-symbol
233 (progn (put file-history-symbol 'cursor-end t)
234 (put file-history-symbol 'no-default t)))
235 ;; $'s should be quoted (against substitute-in-file-name) in file
236 ;; names inserted here
237 (if initial
238 (setq initial (gmhist-quote-dollars (gmhist-unexpand-home initial)))
239 (if insert-default-directory
240 (setq initial (gmhist-quote-dollars (gmhist-unexpand-home dir)))))
241 (let* ((minibuffer-completion-confirm (if (eq mustmatch t) nil t))
242 (minibuffer-completion-table 'read-file-name-internal)
243 (minibuffer-completion-predicate dir)
244 (minibuffer-history-symbol file-history-symbol)
245 (val (gmhist-new-read-from-minibuffer
246 grfn-prompt initial (gmhist-lookup-keymap
247 (if mustmatch
248 gmhist-filename-must-match-map
249 gmhist-filename-completion-map)))))
250
251 (or (and (or (and (stringp initial)
252 (string= initial val))
253 (and (null initial)
254 (zerop (length val))))
255 default)
256 (substitute-in-file-name val))))
257
258 (defun gmhist-unexpand-home (file)
259 ;; Make prompt look nicer by un-expanding home dir.
260 ;; read-file-name does this, too.
261 ;; Avoid clobbering match-data with string-match.
262 (let* ((home (expand-file-name "~/"))
263 (home-len (length home))
264 (file-len (length file)))
265 (if (and home
266 (stringp file)
267 (>= file-len home-len)
268 (string= home (substring file 0 home-len))
269 (eq ?/ (aref file (1- home-len))))
270 (concat "~/" (substring file home-len))
271 file)))
272
273 ; (defun gmhist-quote-dollars (file)
274 ; "Quote `$' as `$$' in FILE to get it past function `substitute-in-file-name.'"
275 ; (apply (function concat)
276 ; (mapcar (function
277 ; (lambda (char)
278 ; (if (= char ?$)
279 ; "$$"
280 ; (vector char))))
281 ; file)))
282 ;; 10000 iterations of (gmhist-quote-dollars "foo") took 19 seconds
283 ;; and *lots* of garbage collections (about a dozen or more)
284
285 ;; This version does not cons and is much faster in the usual case
286 ;; without $ present:
287 ;; 10000 iterations of (gmhist-quote-dollars "foo") took 4 seconds and
288 ;; not a single garbage collection.
289 (defun gmhist-quote-dollars (file)
290 "Quote `$' as `$$' in FILE to get it past function `substitute-in-file-name.'"
291 (let ((pos 0))
292 (while (setq pos (string-match "\\$" file pos))
293 (setq file (concat (substring file 0 pos)
294 "$";; precede by escape character (also a $)
295 (substring file pos))
296 ;; add 2 instead 1 since another $ was inserted
297 pos (+ 2 pos)))
298 file))
299
300
301
302 (defun read-buffer-with-history-in (rbwhi-hist-sym &rest args)
303 "Like read-buffer, but with additional first arg HISTORY-SYMBOL."
304 (let ((buffer-history-symbol rbwhi-hist-sym))
305 (apply 'gmhist-read-buffer args)))
306
307 (defvar buffer-history-symbol 'buffer-history
308 "*If non-nil, it is the name (a symbol) of a variable on which to cons
309 buffer names entered in the minibuffer.")
310
311 (defun gmhist-read-buffer (grb-prompt &optional default existing)
312 "Read a buffer name, maintaining history in buffer-history-symbol and return as string.
313 Args PROMPT &optional DEFAULT EXISTING.
314 Optional arg EXISTING means an existing buffer must be entered."
315 (if (bufferp default);; want string in prompt, not buffer object
316 (setq default (buffer-name default)))
317 (if buffer-history-symbol
318 (put buffer-history-symbol 'default default)) ; also if nil
319 (let* ((minibuffer-history-symbol buffer-history-symbol)
320 (name (gmhist-completing-read
321 grb-prompt
322 ;;(function (lambda (buf) (list (buffer-name buf))))
323 ;; convert to alist in format (BUF-NAME . BUF-OBJ)
324 (mapcar
325 (function (lambda (arg) (cons (buffer-name arg) arg)))
326 (buffer-list))
327 (function (lambda (elt) (get-buffer (car elt))))
328 existing)))
329 (if (equal "" name)
330 default
331 name)))
332
333 (defvar minibuffer-history-symbol 'minibuffer-history
334 "*If non-nil, it is the name (a symbol) of a variable on which to cons
335 the string entered in the minibuffer.
336 Input is stored as string, even for e.g. `read-buffer'.")
337
338 (defvar minibuffer-history nil
339 "List of strings entered using the minibuffer, most recent first.")
340
341 (put 'minibuffer-history 'no-default t)
342
343 (defvar minibuffer-history-read-only nil
344 "If non-nil, nothing will be stored on `minibuffer-history-symbol'.
345 History motions commands are still available in the minibuffer.")
346
347 (defvar minibuffer-history-position nil
348 "If currently reading the minibuffer, the history position.")
349
350 (defvar minibuffer-initial-contents nil
351 "If currently reading the minibuffer, the initial contents.")
352
353 ;; Save the subr, we need it inside the redefined version:
354 (or (fboundp 'gmhist-old-read-from-minibuffer)
355 (fset 'gmhist-old-read-from-minibuffer
356 (symbol-function 'read-from-minibuffer)))
357
358 (defun gmhist-new-read-from-minibuffer
359 (gnrfm-prompt &optional initial-contents keymap read position)
360 "Read a string from the minibuffer, prompting with string PROMPT.
361 If optional second arg INITIAL-CONTENTS is non-nil, it is a string
362 to be inserted into the minibuffer before reading input.
363 Third arg KEYMAP is a keymap to use whilst reading;
364 if omitted or nil, the default is `minibuffer-local-map'.
365 If fourth arg READ is non-nil, then interpret the result as a lisp object
366 and return that object:
367 in other words, do `(car (read-from-string INPUT-STRING))'
368 Fifth arg POSITION, if non-nil, is where to put point
369 in the minibuffer after inserting INITIAL-CONTENTS.
370
371 The ambient value of `minibuffer-history-symbol' (q.v.) is used and set.
372
373 *** This is the gmhist version.***"
374 (if (null minibuffer-history-symbol)
375 (if gmhist-emacs-19-p
376 (gmhist-old-read-from-minibuffer
377 gnrfm-prompt initial-contents keymap read position)
378 (gmhist-old-read-from-minibuffer gnrfm-prompt initial-contents
379 keymap read))
380 (gmhist-read-from-minibuffer
381 gnrfm-prompt initial-contents keymap read position)))
382
383 (defun gmhist-read-from-minibuffer (grfm-prompt
384 &optional
385 initial-contents keymap read position)
386 (or keymap (setq keymap minibuffer-local-map))
387 (or minibuffer-history-read-only
388 (boundp minibuffer-history-symbol) ; history list defaults to nil
389 ;; create history list if not already done
390 (set minibuffer-history-symbol
391 (get minibuffer-history-symbol 'initial-hist)))
392 (let* ((minibuffer-history-position 0) ; fluid var for motion commands
393 (minibuffer-initial-contents initial-contents) ; ditto
394 (history (symbol-value minibuffer-history-symbol))
395 ;; Command is an s-exp when read->t. In this case,
396 ;; cannot have empty input:
397 (no-default (or read
398 (get minibuffer-history-symbol 'no-default)))
399 (dangerous (if no-default
400 nil
401 (get minibuffer-history-symbol 'dangerous)))
402 ;; Idea for 'backup feature by Mike Williams
403 (backup (get minibuffer-history-symbol 'backup))
404 (default (if no-default
405 nil
406 (get minibuffer-history-symbol 'default)))
407 (the-prompt (if default
408 (concat grfm-prompt (format gmhist-default-format
409 default))
410 grfm-prompt))
411 (the-initial (if (or minibuffer-initial-contents
412 (not backup))
413 minibuffer-initial-contents
414 ;; else we must backup in the history list
415 (setq backup (min (max 0 (or (and (integerp backup)
416 backup)
417 1))
418 (length history)))
419 (if (zerop (setq minibuffer-history-position backup))
420 nil
421 ;; else backup is at least 1
422 (let ((backup-input (nth (1- backup) history)))
423 (if read
424 (prin1-to-string backup-input)
425 backup-input)))))
426 command)
427 ;; Read the command from minibuffer, providing history motion
428 ;; key map and minibuffer completion
429 (setq command
430 (if position
431 ;; avoid passing POSITION arg unless given (presumably
432 ;; we are in Emacs 19 then)
433 (gmhist-old-read-from-minibuffer the-prompt the-initial keymap
434 position)
435 (gmhist-old-read-from-minibuffer the-prompt the-initial keymap)))
436 ;; Care about default values unless forbidden:
437 (or no-default
438 (setq command (gmhist-handle-default command default dangerous)))
439 (if minibuffer-history-read-only
440 nil
441 (let (ignore)
442 ;; Add to history if first command, or not a dup, or not to be ignored
443 (or (and history
444 (or (if (get minibuffer-history-symbol 'keep-dups)
445 nil
446 (equal command (car history)))
447 (if (stringp (setq ignore (get minibuffer-history-symbol
448 'hist-ignore)))
449 (string-match ignore
450 (gmhist-stringify (car history))))))
451 (set minibuffer-history-symbol (cons command history)))))
452 ;; Return command's value to caller:
453 (if read
454 (car (read-from-string command))
455 command)))
456
457 (defun gmhist-handle-default (command default dangerous)
458 (if (string= "" command)
459 (if default (setq command default)))
460 ;; Set default value unless it is dangerous.
461 (or (and (stringp dangerous)
462 ;; Should actually save match-data as we call string-match
463 (string-match dangerous (gmhist-stringify command)))
464 (put minibuffer-history-symbol 'default command))
465 ;; Return the prefrobnicated command:
466 command)
467
468
469 ;; Minibuffer key maps to implement history
470
471 (defvar gmhist-define-keys-hook nil
472 "Hook run inside function `gmhist-define-keys' (q.v.), after the
473 standard gmhist bindings.")
474
475 (or (fboundp 'gmhist-define-keys)
476 (defun gmhist-define-keys (map)
477 "Bind the standard history commands in MAP, a key map.
478
479 When gmhist is loaded, this function is only defined if you have not
480 already defined it, so that you can customize it without worrying
481 about load order.
482 You can also use `gmhist-define-keys-hook' if you just want to add to
483 existing bindings."
484 (define-key map "\M-p" 'gmhist-previous)
485 (define-key map "\M-n" 'gmhist-next)
486 (define-key map "\M-r" 'gmhist-search-backward)
487 (define-key map "\M-s" 'gmhist-search-forward)
488 ;;(define-key map "\M-<" 'gmhist-beginning)
489 ;;(define-key map "\M-<" 'gmhist-beginning)
490 ;; Last two for bash/readline compatibility. Better M-a and M-e ?
491 ;; In query-replace, multi-line text together with next-line's
492 ;; misfeature of adding blank lines really lets you lose without M-<
493 ;; and M->.
494 ;;(define-key map "\M-a" 'gmhist-beginning)
495 ;;(define-key map "\M-e" 'gmhist-end)
496 ;; M-a is already used in electric replace
497 ;; Try this as general purpose mover:
498 (define-key map "\M-g" 'gmhist-toggle)
499 (define-key map "\M-G" 'gmhist-switch-history)
500 (define-key map "\M-?" 'gmhist-show)
501 (run-hooks 'gmhist-define-keys-hook)))
502
503 (defun gmhist-lookup-keymap (map)
504 (if (keymapp map)
505 map
506 (gmhist-lookup-keymap (symbol-value map))))
507
508 (defvar gmhist-map nil
509 "Key map for generic minibuffer history.
510 \\<gmhist-map>\\[gmhist-previous], \\[gmhist-next], \
511 \\[gmhist-beginning], \\[gmhist-end] move through, \
512 \\[gmhist-search-backward] and \\[gmhist-search-forward] search,
513 \\[gmhist-show] displays the history:
514 \\{gmhist-map}")
515
516 (if gmhist-map
517 nil
518 (setq gmhist-map (copy-keymap minibuffer-local-map))
519 (gmhist-define-keys gmhist-map))
520
521 (defvar gmhist-completion-map nil
522 "Key map for generic minibuffer history with completion, see gmhist-map.")
523
524 (if gmhist-completion-map
525 nil
526 ;; If you have loaded D. Gillespie's complete.el or Christopher
527 ;; McConnell's completer.el *before* gmhist, you get it in gmhist,
528 ;; too:
529 (setq gmhist-completion-map (copy-keymap minibuffer-local-completion-map))
530 (gmhist-define-keys gmhist-completion-map))
531
532 (defvar gmhist-must-match-map nil
533 "Key map for generic minibuffer history with completion that must match,
534 see gmhist-map.")
535
536 (if gmhist-must-match-map
537 nil
538 (setq gmhist-must-match-map (copy-keymap minibuffer-local-must-match-map))
539 (gmhist-define-keys gmhist-must-match-map))
540
541 (defvar gmhist-filename-completion-map 'gmhist-completion-map
542 "A keymap (or a symbol pointing to one) to use in filename
543 completion that need not match. Defaults to 'gmhist-completion-map.")
544
545 (defvar gmhist-filename-must-match-map 'gmhist-must-match-map
546
547 "A keymap (or a symbol pointing to one) to use in filename
548 completion that must match. Defaults to 'gmhist-must-match-map.")
549
550
551 ;; Minibuffer commands to implement history
552 ;; They run inside read-with-history-in and heavily depend on fluid
553 ;; vars from there.
554
555 (defun gmhist-goto (n)
556 ;; Go to history position N, 1 <= N <= length of history
557 ;; N<0 means the future and inserts an empty string
558 ;; N=0 means minibuffer-initial-contents (fluid var from
559 ;; gmhist-new-read-from-minibuffer)
560 (run-hooks 'gmhist-before-move-hook)
561 (erase-buffer)
562 (setq minibuffer-history-position n)
563 (if (< n 0)
564 nil
565 (insert
566 (gmhist-stringify
567 (if (= n 0)
568 (or minibuffer-initial-contents "")
569 (nth (1- n) (symbol-value minibuffer-history-symbol)))))
570 (run-hooks 'gmhist-after-insert-hook)
571 ;; next two actually would be a good application for this hook
572 (goto-char (if (get minibuffer-history-symbol 'cursor-end)
573 (point-max)
574 (point-min)))
575 (let ((pos (get minibuffer-history-symbol 'cursor-pos)))
576 (if (stringp pos)
577 (if (eobp)
578 (re-search-backward pos nil t)
579 (re-search-forward pos nil t))))))
580
581 (defun gmhist-beginning ()
582 "Go to the oldest command in the history."
583 (interactive)
584 (gmhist-goto (length (symbol-value minibuffer-history-symbol))))
585
586 (defun gmhist-end ()
587 "Position before the most recent command in the history."
588 (interactive)
589 (gmhist-goto 0))
590
591 (defun gmhist-toggle (&optional n)
592 "If at end of history, move to beginning, else move to end.
593 Prefix arg is history position to go to."
594 (interactive "P")
595 (if n
596 (gmhist-goto (prefix-numeric-value n))
597 (if (= 0 minibuffer-history-position)
598 (gmhist-beginning)
599 (gmhist-end))))
600
601 (defun gmhist-switch-history (new-history)
602 "Switch to a different history."
603 (interactive
604 (let ((enable-recursive-minibuffers t))
605 (list (read-from-minibuffer "Switch to history: " nil nil t))))
606 (setq minibuffer-history-symbol new-history
607 minibuffer-history-position 0))
608
609 (defun gmhist-next (n)
610 "Go to next history position."
611 ;; fluid vars: minibuffer-history-symbol minibuffer-history-position
612 ;; Inserts the next element of minibuffer-history-symbol's value
613 ;; into the minibuffer.
614 ;; minibuffer-history-position is the current history position.
615 (interactive "p")
616 ;; clip the new history position to the valid range:
617 (let ((narg (min (max 0 (- minibuffer-history-position n))
618 (length (symbol-value minibuffer-history-symbol)))))
619 (if (= minibuffer-history-position narg)
620 (error "No %s item in %s"
621 (if (= 0 minibuffer-history-position) "following" "preceding")
622 minibuffer-history-symbol)
623 (gmhist-goto narg))))
624
625 (defun gmhist-previous (n)
626 "Go to previous history position."
627 (interactive "p")
628 (gmhist-next (- n)))
629
630 ;; Searching the history
631
632 (defun gmhist-search-backward (regexp &optional forward)
633 "Search backward in the history list for REGEXP.
634 With prefix argument, search for line that contains match for current line."
635 (interactive
636 (if current-prefix-arg
637 (list (regexp-quote (buffer-string)))
638 (let ((enable-recursive-minibuffers t))
639 (list (read-with-history-in 'gmhist-search-history
640 "History search (regexp): ")))))
641 (let* (found
642 (direction (if forward -1 1))
643 (pos (+ minibuffer-history-position direction)) ; find _next_ match!
644 (history (symbol-value minibuffer-history-symbol))
645 (len (length history)))
646 (while (and (if forward (> pos 0) (<= pos len))
647 (not (setq found
648 (string-match
649 regexp
650 (gmhist-stringify (nth (1- pos) history))))))
651 (setq pos (+ pos direction)))
652 (or found (error "%s not found in %s" regexp minibuffer-history-symbol))
653 (gmhist-goto pos)))
654
655 (defun gmhist-search-forward (regexp &optional backward)
656 "Search forward in the history list for REGEXP.
657 With prefix argument, search for line that matches current line
658 instead of prompting for REGEXP."
659 (interactive
660 (if current-prefix-arg
661 (list (regexp-quote (buffer-string)))
662 (let ((enable-recursive-minibuffers t))
663 (list (read-with-history-in 'gmhist-search-history
664 "History search forward (regexp): ")))))
665 (gmhist-search-backward regexp (not backward)))
666
667 ;; Misc.
668
669 (defun gmhist-stringify (elt)
670 ;; If ELT is not a string, convert it to one.
671 (if (stringp elt) elt (prin1-to-string elt)))
672
673 (defun gmhist-show ()
674 "Show the history list in another buffer.
675 Use \\[scroll-other-window] to scroll, with negative arg to scroll back."
676 (interactive)
677 (let ((count 0))
678 (with-output-to-temp-buffer (concat "*" (symbol-name minibuffer-history-symbol) "*")
679 (mapcar
680 (function
681 (lambda (x)
682 (princ (format "%2s%2d: %s\n"
683 (if (eq (setq count (1+ count))
684 minibuffer-history-position)
685 "> "
686 " ")
687 count x))))
688 (symbol-value minibuffer-history-symbol)))))
689
690 (defun gmhist-remember-zero ()
691 "Put this function on gmhist-before-move-hook to make gmhist
692 remember the initial value even after you edited it:
693
694 (setq gmhist-before-move-hook 'gmhist-remember-zero)"
695 (if (zerop minibuffer-history-position)
696 (setq minibuffer-initial-contents (buffer-string))))
697
698 ;; Hack up interactive specifications of existing functions
699
700 (defun gmhist-copy-function (fun)
701 (let ((old (gmhist-symbol-function fun)))
702 (if (consp old) ; interpreted, or v18 compiled
703 ;; copy-sequence does not copy recursively.
704 ;; Iteration is faster than recursion, and we need just two levels
705 ;; to be able to use setcdr to mung the interactive spec.
706 (let (new elt)
707 (while old
708 (setq elt (car old)
709 old (cdr old)
710 new (cons (if (sequencep elt)
711 (copy-sequence elt)
712 elt)
713 new)))
714 (nreverse new))
715 ;; else v19 compiled
716 (let ((new (append old nil)))
717 (setcar (nthcdr 5 new) (copy-sequence (aref old 5)))
718 (apply 'make-byte-code new)))))
719
720 (defun gmhist-check-autoload (fun)
721 "If FUN is an autoload, load its definition."
722 (let ((lis (symbol-function fun)))
723 (if (and (listp lis) ; FUN could also be a subr
724 (eq 'autoload (car lis)))
725 (load (nth 1 lis)))))
726
727 (defun gmhist-replace-spec (fun new-spec &optional copy-first)
728 "Replace the interactive specification of FUN with NEW-SPEC.
729 FUN must be a symbol with a function definition.
730 Autoload functions are taken care of by loading the appropriate file first.
731 If FUN is a pure storage function (one dumped into Emacs) it is first
732 copied onto itself, because pure storage cannot be modified.
733 Optional non-nil third arg COPY-FIRST is used internally for this.
734 The old spec is put on FUN's gmhist-old-interactive-spec property.
735 That property is never overwritten by this function. It is used by
736 function gmhist-remove-magic."
737 (gmhist-check-autoload fun)
738 (if copy-first ; copy (from pure storage)
739 (fset fun (gmhist-copy-function fun)))
740 (let* ((flambda (gmhist-symbol-function fun))
741 (fint (and (consp flambda)
742 (if (eq 'interactive (car-safe (nth 2 flambda)))
743 (nth 2 flambda)
744 (if (eq 'interactive (car-safe (nth 3 flambda)))
745 (nth 3 flambda)
746 (error "%s is not interactive" fun)))))
747 (old-spec (if fint
748 (nth 1 fint)
749 (gmhist-spec fun))))
750 ;; Save old interactive spec as property of FUN:
751 (or (get fun 'gmhist-old-interactive-spec)
752 (put fun 'gmhist-old-interactive-spec old-spec))
753 ;; Replace '(interactive OLD-SPEC) with '(interactive NEW-SPEC)
754 (if copy-first
755 ;; This should not fail - if it does, we must abort.
756 (if (consp flambda)
757 (setcdr fint (list new-spec))
758 ;; can't "aset" a #<byte-code> object, though aref works...
759 (setq flambda (append flambda nil))
760 (setcar (nthcdr 5 flambda) new-spec)
761 (setq flambda (apply 'make-byte-code flambda))
762 (fset fun flambda))
763 ;; else prepare for a second try
764 (condition-case err
765 (setcdr fint (list new-spec))
766 (error
767 ;; Setcdr bombs on preloaded functions:
768 ;; (error "Attempt to modify read-only object")
769 ;; There seems to be no simple way to test whether an object
770 ;; resides in pure storage, so we let it bomb and try again
771 ;; after copying it into writable storage.
772 (gmhist-replace-spec fun new-spec t))))))
773
774 (defun gmhist-spec (fun)
775 "Get the current interactive specification for FUN (a symbol).
776 Signal an error if FUN is not interactive."
777 (let ((flambda (gmhist-symbol-function fun))
778 fint)
779 (cond ((consp flambda) ; interpreted, or v18 compiled
780 ;; do it exactly like call-interactively, even if this
781 ;; means (interactive...) can come arbitrary late in FUN's body
782 (setq fint (assq 'interactive (cdr (cdr flambda))))
783 (or fint
784 (error "Cannot get spec of a non-interactive command: %s!" fun))
785 (nth 1 fint))
786 (t ; otherwise it's a v19 compiled-code object
787 (aref flambda 5)))))
788
789 (defun gmhist-symbol-function (fun)
790 ;; Return FUN's ultimate definition.
791 ;; Recurse if FUN is fset to another function's name.
792 (let ((flambda (symbol-function fun)))
793 (if (symbolp flambda)
794 ;; Prefer recursion over while because infinite loop is caught
795 ;; by max-lisp-eval-depth.
796 (gmhist-symbol-function flambda)
797 flambda)))
798
799 ;; Automagic gmhistification
800
801 ;; There should be a builtin split function - inverse to mapconcat.
802 (defun gmhist-split (pat str &optional limit)
803 "Splitting on regexp PAT, turn string STR into a list of substrings.
804 Optional third arg LIMIT (>= 1) is a limit to the length of the
805 resulting list.
806 Thus, if SEP is a regexp that only matches itself,
807
808 (mapconcat 'identity (gmhist-split SEP STRING) SEP)
809
810 is always equal to STRING."
811 (let* ((start (string-match pat str))
812 (result (list (substring str 0 start)))
813 (count 1)
814 (end (if start (match-end 0))))
815 (if end ; else nothing left
816 (while (and (or (not (integerp limit))
817 (< count limit))
818 (string-match pat str end))
819 (setq start (match-beginning 0)
820 count (1+ count)
821 result (cons (substring str end start) result)
822 end (match-end 0)
823 start end)
824 ))
825 (if (and (or (not (integerp limit))
826 (< count limit))
827 end) ; else nothing left
828 (setq result
829 (cons (substring str end) result)))
830 (nreverse result)))
831
832 (defun gmhist-interactive (spec hist)
833 "Interpret SPEC, an interactive string, like call-interactively
834 would, only with minibuffer history in HIST (a symbol).
835
836 If the value of HIST is another symbol (which can never happen if
837 history lists are already stored on it), this symbol is taken instead
838 to facilitate dynamic indirections.
839
840 Currently recognized key letters are:
841
842 a b B c C d D k m N n s S x X f F r p P v
843
844 and initial `*'.
845
846 Use it inside interactive like this
847
848 \(interactive \(gmhist-interactive \"sPrompt: \\nP\" 'foo-history\)\)
849
850 or even like this:
851
852 \(interactive
853 \(gmhist-interactive \"sReplace: \\nsReplace %s with: \" 'replace-history\)\)
854 "
855 (or (stringp spec)
856 (error "gmhist-interactive: not a string %s" spec))
857 (if (and (> (length spec) 0) (eq ?\* (aref spec 0)))
858 (progn
859 (barf-if-buffer-read-only)
860 (setq spec (substring spec 1))))
861 (if (and (boundp hist)
862 (symbolp (symbol-value hist))
863 (not (null (symbol-value hist))))
864 (setq hist (symbol-value hist)))
865 (let ((spec-list (mapcar '(lambda (x)
866 ;; forgive empty entries like
867 ;; call-interactively does:
868 (if (equal "" x)
869 nil
870 (cons (aref x 0) (substring x 1))))
871 (gmhist-split "\n" spec)))
872 cur-arg args-so-far special elt char prompt xprompt)
873 (setq spec-list (delq nil spec-list))
874 (while spec-list
875 (setq elt (car spec-list)
876 spec-list (cdr spec-list)
877 special nil ; special handling of args-so-far
878 char (car elt)
879 prompt (cdr elt)
880 xprompt (apply (function format) prompt (reverse args-so-far)))
881 (cond ((eq char ?a) ; Symbol defined as a function
882 (setq cur-arg (intern
883 (completing-read-with-history-in
884 hist xprompt obarray 'fboundp t nil))))
885 ((eq char ?b) ; Name of existing buffer
886 (setq cur-arg (read-buffer-with-history-in
887 hist xprompt (other-buffer) t)))
888 ((eq char ?B) ; Name of possibly non-existing buffer
889 (setq cur-arg (read-buffer-with-history-in
890 hist xprompt (other-buffer) nil)))
891 ((eq char ?c) ; Character
892 (message xprompt) ; history doesn't make sense for this
893 (setq cur-arg (read-char)))
894 ((eq char ?C) ; Command
895 (setq cur-arg (intern
896 (completing-read-with-history-in
897 hist xprompt obarray 'commandp t nil))))
898 ((eq char ?d) ; Value of point. Does not do I/O.
899 (setq cur-arg (point)))
900 ((eq char ?D) ; directory name
901 ;; This does not check file-directory-p, but neither does
902 ;; call-interactively.
903 (setq cur-arg (read-file-name-with-history-in
904 hist
905 xprompt
906 nil
907 default-directory
908 'confirm)))
909 ((eq char ?f) ; existing file name
910 (setq cur-arg (read-file-name-with-history-in
911 hist
912 xprompt
913 nil nil 'confirm)))
914 ((eq char ?F) ; possibly nonexistent file name
915 (setq cur-arg (read-file-name-with-history-in
916 hist
917 xprompt)))
918 ((eq char ?k) ; Key sequence (string)
919 (setq cur-arg (read-key-sequence (if (equal xprompt "")
920 nil xprompt))))
921 ((eq char ?m) ; Value of mark. Does not do I/O.
922 (setq cur-arg (or (mark) (error "The mark is not set now"))))
923 ((eq char ?N) ; Prefix arg, else number from minibuf
924 (if current-prefix-arg
925 (setq cur-arg (prefix-numeric-value current-prefix-arg))
926 (while (not (integerp
927 (setq cur-arg
928 (read-with-history-in hist xprompt nil t)))))))
929 ((eq char ?n) ; Read number from minibuffer
930 (while (not (integerp
931 (setq cur-arg
932 (read-with-history-in hist xprompt nil t))))))
933 ((eq char ?p) ; cooked prefix arg
934 (setq cur-arg (prefix-numeric-value current-prefix-arg)))
935 ((eq char ?P) ; raw prefix arg
936 (setq cur-arg current-prefix-arg))
937 ((eq char ?r) ; region
938 (let (region-min region-max)
939 ;; take some pains to behave exactly like interactive "r"
940 (setq region-min (min (or (mark)
941 (error "The mark is not set now"))
942 (point))
943 region-max (max (or (mark)
944 (error "The mark is not set now"))
945 (point)))
946 (setq args-so-far
947 (append (list region-max region-min) args-so-far)
948 special t)))
949 ((eq char '?s) ; string
950 (setq cur-arg (read-with-history-in hist xprompt)))
951 ((eq char ?S) ; any symbol
952 (setq cur-arg (read-with-history-in hist xprompt nil t)))
953 ((eq char ?v) ; Variable name
954 (setq cur-arg (completing-read-with-history-in
955 hist xprompt obarray 'user-variable-p t nil)))
956 ((memq char '(?x ?X)) ; lisp expression
957 (setq cur-arg (read-with-history-in
958 hist
959 xprompt
960 nil
961 ;; have to tell gmhist to read s-exps
962 ;; instead of strings:
963 t))
964 (if (eq char ?X) ; lisp expression, evaluated
965 (setq cur-arg (eval cur-arg))))
966
967 (t
968 (error "Invalid control letter `%c' in gmhist-interactive" char)))
969 (or special
970 (setq args-so-far (cons cur-arg args-so-far))))
971 (reverse args-so-far)))
972
973 (defun gmhist-new-spec (fun &optional hist no-error)
974 "Return a new interactive specification for FUN, suitable for use
975 with setcdr in function gmhist-replace-spec.
976 Use symbol HIST to store the history. HIST defaults to `FUN-history'.
977 The returned spec does the same as the old one, only with history in HIST.
978
979 If FUN is an autoload object, its file is loaded first.
980
981 See function gmhist-interactive for a list of recognized interactive
982 keys letters.
983
984 Unless optional third arg NO-ERROR is given, signals an error if FUN's
985 interactive string contains unknown key letters or has no interactive string.
986 With NO-ERROR, it returns nil."
987 (or hist (setq hist (intern (concat (symbol-name fun) "-history"))))
988 (gmhist-check-autoload fun)
989 (let ((spec (gmhist-spec fun)))
990 (if (stringp spec)
991 (list 'gmhist-interactive spec (list 'quote hist))
992 (if no-error
993 nil
994 (error "Can't gmhistify %s's spec: %s" fun spec)))))
995
996 (defun gmhist-make-magic (fun &optional hist)
997 "Make FUN magically maintain minibuffer history in symbol HIST.
998 HIST defaults to `FUN-history'.
999 This works by modifying the interactive specification, which must be a
1000 string. For more complicated cases, see gmhist-replace-spec.
1001 The magic goes away when you call gmhist-remove-magic on FUN."
1002 (interactive "CPut gmhist magic on command: ")
1003 (let ((new-spec (gmhist-new-spec fun hist t)))
1004 (if new-spec
1005 (gmhist-replace-spec fun new-spec)
1006 ;; else there was some error. Try to find out if this is a retry.
1007 (if (not (get fun 'gmhist-old-interactive-spec))
1008 (error "Too complicated for gmhist: %s" fun)
1009 (message "Another attempt to put magic on %s..." fun)
1010 (gmhist-remove-magic fun) ; will abort if not a retry
1011 ;; This time we don't catch errors - magic or blow!
1012 (gmhist-replace-spec fun (gmhist-new-spec fun hist))
1013 (message "Another attempt to put magic on %s...done." fun)))))
1014
1015 (defun gmhist-remove-magic (fun)
1016 "Remove the magic that gmhist-make-magic put on FUN,
1017 restoring the old interactive spec."
1018 (interactive "CRemove gmhist magic from command: ")
1019 (gmhist-replace-spec
1020 fun
1021 (or (get fun 'gmhist-old-interactive-spec)
1022 (error "Can't find %s's old interactive spec!" fun))))
1023
1024 ;; Now make yourself magic
1025 (gmhist-make-magic 'gmhist-make-magic 'gmhist-make-magic-history)
1026 (gmhist-make-magic 'gmhist-remove-magic 'gmhist-make-magic-history)
1027
1028
1029 ;; Examples, pedagogic and serious ones. More in gmhist-app.el.
1030
1031 ;;(defun foo-command (cmd)
1032 ;; (interactive (list
1033 ;; (read-with-history-in 'foo-history "Foo: ")))
1034 ;; (message "Foo %s" cmd))
1035 ;;
1036 ;; ;; The interactive clause could also have been the simpler
1037 ;; ;; (interactive (gmhist-interactive "sFoo: " 'foo-history))
1038 ;;
1039 ;;
1040 ;;;(put 'foo-history 'hist-map minibuffer-local-map) ; disable motion ...
1041 ;;;(put 'foo-history 'hist-function 'gmhist-read-nohistory) ; and history
1042 ;;
1043 ;;(put 'foo-history 'hist-function nil) ; enable history ...
1044 ;;(put 'foo-history 'hist-map nil) ; and motion again
1045 ;;
1046 ;;(defun gmhist-read-nohistory (symbol prompt initial-input read)
1047 ;; "An example function to put on the hist-function property."
1048 ;; (message "read-nohistory...")
1049 ;; (sit-for 2)
1050 ;; (read-string prompt initial-input))
1051 ;;
1052 ;; Example for reading file names:
1053 ;;(defun bar-command (cmd)
1054 ;; (interactive
1055 ;; (list
1056 ;; (read-file-name-with-history-in
1057 ;; ;; HIST-SYM PROMPT DIR DFLT MUSTMATCH
1058 ;; 'bar-history "Bar: " nil nil 'confirm)))
1059 ;; (message "Bar %s" cmd))
1060 ;;
1061 ;; Example function to apply gmhist-make-magic to.
1062 ;; Compare the missing initial input in bar to the magic version of zod.
1063 ;;(defun zod-command (cmd)
1064 ;; (interactive "fZod: ")
1065 ;; (message "Zod %s" cmd))
1066
1067 ;; Finally run the load-hook
1068
1069 (run-hooks 'gmhist-load-hook)
1070
1071 ;; End of file gmhist.el