]> code.delx.au - gnu-emacs/blob - lisp/eshell/em-hist.el
*** empty log message ***
[gnu-emacs] / lisp / eshell / em-hist.el
1 ;;; em-hist --- history list management
2
3 ;; Copyright (C) 1999, 2000 Free Software Foundation
4
5 ;; This file is part of GNU Emacs.
6
7 ;; GNU Emacs is free software; you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation; either version 2, or (at your option)
10 ;; any later version.
11
12 ;; GNU Emacs is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
16
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with GNU Emacs; see the file COPYING. If not, write to the
19 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 ;; Boston, MA 02111-1307, USA.
21
22 (provide 'em-hist)
23
24 (eval-when-compile (require 'esh-maint))
25
26 (defgroup eshell-hist nil
27 "This module provides command history management."
28 :tag "History list management"
29 :group 'eshell-module)
30
31 ;;; Commentary:
32
33 ;; Eshell's history facility imitates the syntax used by bash
34 ;; ([(bash)History Interaction]). Thus:
35 ;;
36 ;; !ls ; repeat the last command beginning with 'ls'
37 ;; !?ls ; repeat the last command containing ls
38 ;; echo !ls:2 ; echo the second arg of the last 'ls' command
39 ;; !ls<tab> ; complete against all possible words in this
40 ;; ; position, by looking at the history list
41 ;; !ls<C-c SPC> ; expand any matching history input at point
42 ;;
43 ;; Also, most of `comint-mode's keybindings are accepted:
44 ;;
45 ;; M-r ; search backward for a previous command by regexp
46 ;; M-s ; search forward for a previous command by regexp
47 ;; M-p ; access the last command entered, repeatable
48 ;; M-n ; access the first command entered, repeatable
49 ;;
50 ;; C-c M-r ; using current input, find a matching command thus, with
51 ;; ; 'ls' as the current input, it will go back to the same
52 ;; ; command that '!ls' would have selected
53 ;; C-c M-s ; same, but in reverse order
54 ;;
55 ;; Note that some of these keybindings are only available if the
56 ;; `eshell-rebind' is not in use, in which case M-p does what C-c M-r
57 ;; normally would do, and C-p is used instead of M-p. It may seem
58 ;; confusing, but the intention is to make the most useful
59 ;; functionality the most easily accessible. If `eshell-rebind' is
60 ;; not being used, history navigation will use comint's keybindings;
61 ;; if it is, history navigation tries to use similar keybindings to
62 ;; bash. This is all configurable, of course.
63
64 ;;; Code:
65
66 (require 'ring)
67 (require 'esh-opt)
68 (require 'em-pred)
69
70 ;;; User Variables:
71
72 (defcustom eshell-hist-load-hook '(eshell-hist-initialize)
73 "*A list of functions to call when loading `eshell-hist'."
74 :type 'hook
75 :group 'eshell-hist)
76
77 (defcustom eshell-hist-unload-hook
78 (list
79 (function
80 (lambda ()
81 (remove-hook 'kill-emacs-hook 'eshell-save-some-history))))
82 "*A hook that gets run when `eshell-hist' is unloaded."
83 :type 'hook
84 :group 'eshell-hist)
85
86 (defcustom eshell-history-file-name
87 (concat eshell-directory-name "history")
88 "*If non-nil, name of the file to read/write input history.
89 See also `eshell-read-history' and `eshell-write-history'.
90 If it is nil, Eshell will use the value of HISTFILE."
91 :type 'file
92 :group 'eshell-hist)
93
94 (defcustom eshell-history-size 128
95 "*Size of the input history ring. If nil, use envvar HISTSIZE."
96 :type 'integer
97 :group 'eshell-hist)
98
99 (defcustom eshell-hist-ignoredups nil
100 "*If non-nil, don't add input matching the last on the input ring.
101 This mirrors the optional behavior of bash."
102 :type 'boolean
103 :group 'eshell-hist)
104
105 (defcustom eshell-ask-to-save-history t
106 "*Determine if history should be automatically saved.
107 History is always preserved after sanely exiting an Eshell buffer.
108 However, when Emacs is being shut down, this variable determines
109 whether to prompt the user.
110 If set to nil, it means never ask whether history should be saved.
111 If set to t, always ask if any Eshell buffers are open at exit time.
112 If set to `always', history will always be saved, silently."
113 :type '(choice (const :tag "Never" nil)
114 (const :tag "Ask" t)
115 (const :tag "Always save" always))
116 :group 'eshell-hist)
117
118 (defcustom eshell-input-filter
119 (function
120 (lambda (str)
121 (not (string-match "\\`\\s-*\\'" str))))
122 "*Predicate for filtering additions to input history.
123 Takes one argument, the input. If non-nil, the input may be saved on
124 the input history list. Default is to save anything that isn't all
125 whitespace."
126 :type 'function
127 :group 'eshell-hist)
128
129 (put 'eshell-input-filter 'risky-local-variable t)
130
131 (defcustom eshell-hist-match-partial t
132 "*If non-nil, movement through history is constrained by current input.
133 Otherwise, typing <M-p> and <M-n> will always go to the next history
134 element, regardless of any text on the command line. In that case,
135 <C-c M-r> and <C-c M-s> still offer that functionality."
136 :type 'boolean
137 :group 'eshell-hist)
138
139 (defcustom eshell-hist-move-to-end t
140 "*If non-nil, move to the end of the buffer before cycling history."
141 :type 'boolean
142 :group 'eshell-hist)
143
144 (defcustom eshell-hist-event-designator
145 "^!\\(!\\|-?[0-9]+\\|\\??[^:^$%*?]+\\??\\|#\\)"
146 "*The regexp used to identifier history event designators."
147 :type 'regexp
148 :group 'eshell-hist)
149
150 (defcustom eshell-hist-word-designator
151 "^:?\\([0-9]+\\|[$^%*]\\)?\\(\\*\\|-[0-9]*\\|[$^%*]\\)?"
152 "*The regexp used to identify history word designators."
153 :type 'regexp
154 :group 'eshell-hist)
155
156 (defcustom eshell-hist-modifier
157 "^\\(:\\([hretpqx&g]\\|s/\\([^/]*\\)/\\([^/]*\\)/\\)\\)*"
158 "*The regexp used to identity history modifiers."
159 :type 'regexp
160 :group 'eshell-hist)
161
162 (defcustom eshell-hist-rebind-keys-alist
163 '(([(control ?p)] . eshell-previous-input)
164 ([(control ?n)] . eshell-next-input)
165 ([(control up)] . eshell-previous-input)
166 ([(control down)] . eshell-next-input)
167 ([(control ?r)] . eshell-isearch-backward)
168 ([(control ?s)] . eshell-isearch-forward)
169 ([(meta ?r)] . eshell-previous-matching-input)
170 ([(meta ?s)] . eshell-next-matching-input)
171 ([(meta ?p)] . eshell-previous-matching-input-from-input)
172 ([(meta ?n)] . eshell-next-matching-input-from-input)
173 ([up] . eshell-previous-matching-input-from-input)
174 ([down] . eshell-next-matching-input-from-input))
175 "*History keys to bind differently if point is in input text."
176 :type '(repeat (cons (vector :tag "Keys to bind"
177 (repeat :inline t sexp))
178 (function :tag "Command")))
179 :group 'eshell-hist)
180
181 ;;; Internal Variables:
182
183 (defvar eshell-history-ring nil)
184 (defvar eshell-history-index nil)
185 (defvar eshell-matching-input-from-input-string "")
186 (defvar eshell-save-history-index nil)
187
188 (defvar eshell-isearch-map nil)
189
190 (unless eshell-isearch-map
191 (setq eshell-isearch-map (copy-keymap isearch-mode-map))
192 (define-key eshell-isearch-map [(control ?m)] 'eshell-isearch-return)
193 (define-key eshell-isearch-map [return] 'eshell-isearch-return)
194 (define-key eshell-isearch-map [(control ?r)] 'eshell-isearch-repeat-backward)
195 (define-key eshell-isearch-map [(control ?s)] 'eshell-isearch-repeat-forward)
196 (define-key eshell-isearch-map [(control ?g)] 'eshell-isearch-abort)
197 (define-key eshell-isearch-map [backspace] 'eshell-isearch-delete-char)
198 (define-key eshell-isearch-map [delete] 'eshell-isearch-delete-char)
199 (defvar eshell-isearch-cancel-map)
200 (define-prefix-command 'eshell-isearch-cancel-map)
201 (define-key eshell-isearch-map [(control ?c)] 'eshell-isearch-cancel-map)
202 (define-key eshell-isearch-cancel-map [(control ?c)] 'eshell-isearch-cancel))
203
204 ;;; Functions:
205
206 (defun eshell-hist-initialize ()
207 "Initialize the history management code for one Eshell buffer."
208 (make-local-hook 'eshell-expand-input-functions)
209 (add-hook 'eshell-expand-input-functions
210 'eshell-expand-history-references nil t)
211
212 (when (eshell-using-module 'eshell-cmpl)
213 (make-local-hook 'pcomplete-try-first-hook)
214 (add-hook 'pcomplete-try-first-hook
215 'eshell-complete-history-reference nil t))
216
217 (if (eshell-using-module 'eshell-rebind)
218 (let ((rebind-alist (symbol-value 'eshell-rebind-keys-alist)))
219 (make-local-variable 'eshell-rebind-keys-alist)
220 (set 'eshell-rebind-keys-alist
221 (append rebind-alist eshell-hist-rebind-keys-alist))
222 (set (make-local-variable 'search-invisible) t)
223 (set (make-local-variable 'search-exit-option) t)
224 (make-local-hook 'isearch-mode-hook)
225 (add-hook 'isearch-mode-hook
226 (function
227 (lambda ()
228 (if (>= (point) eshell-last-output-end)
229 (setq overriding-terminal-local-map
230 eshell-isearch-map)))) nil t)
231 (make-local-hook 'isearch-mode-end-hook)
232 (add-hook 'isearch-mode-end-hook
233 (function
234 (lambda ()
235 (setq overriding-terminal-local-map nil))) nil t))
236 (define-key eshell-mode-map [up] 'eshell-previous-matching-input-from-input)
237 (define-key eshell-mode-map [down] 'eshell-next-matching-input-from-input)
238 (define-key eshell-mode-map [(control up)] 'eshell-previous-input)
239 (define-key eshell-mode-map [(control down)] 'eshell-next-input)
240 (define-key eshell-mode-map [(meta ?r)] 'eshell-previous-matching-input)
241 (define-key eshell-mode-map [(meta ?s)] 'eshell-next-matching-input)
242 (define-key eshell-command-map [(meta ?r)]
243 'eshell-previous-matching-input-from-input)
244 (define-key eshell-command-map [(meta ?s)]
245 'eshell-next-matching-input-from-input)
246 (if eshell-hist-match-partial
247 (progn
248 (define-key eshell-mode-map [(meta ?p)]
249 'eshell-previous-matching-input-from-input)
250 (define-key eshell-mode-map [(meta ?n)]
251 'eshell-next-matching-input-from-input)
252 (define-key eshell-command-map [(meta ?p)] 'eshell-previous-input)
253 (define-key eshell-command-map [(meta ?n)] 'eshell-next-input))
254 (define-key eshell-mode-map [(meta ?p)] 'eshell-previous-input)
255 (define-key eshell-mode-map [(meta ?n)] 'eshell-next-input)
256 (define-key eshell-command-map [(meta ?p)]
257 'eshell-previous-matching-input-from-input)
258 (define-key eshell-command-map [(meta ?n)]
259 'eshell-next-matching-input-from-input)))
260
261 (make-local-variable 'eshell-history-size)
262 (or eshell-history-size
263 (setq eshell-history-size (getenv "HISTSIZE")))
264
265 (make-local-variable 'eshell-history-file-name)
266 (or eshell-history-file-name
267 (setq eshell-history-file-name (getenv "HISTFILE")))
268
269 (make-local-variable 'eshell-history-index)
270 (make-local-variable 'eshell-save-history-index)
271 (make-local-variable 'eshell-history-ring)
272 (if eshell-history-file-name
273 (eshell-read-history nil t))
274 (unless eshell-history-ring
275 (setq eshell-history-ring (make-ring eshell-history-size)))
276
277 (make-local-hook 'eshell-exit-hook)
278 (add-hook 'eshell-exit-hook 'eshell-write-history nil t)
279
280 (add-hook 'kill-emacs-hook 'eshell-save-some-history)
281
282 (make-local-variable 'eshell-input-filter-functions)
283 (add-hook 'eshell-input-filter-functions 'eshell-add-to-history nil t)
284
285 (define-key eshell-command-map [(control ?l)] 'eshell-list-history)
286 (define-key eshell-command-map [(control ?x)] 'eshell-get-next-from-history))
287
288 (defun eshell-save-some-history ()
289 "Save the history for any open Eshell buffers."
290 (eshell-for buf (buffer-list)
291 (if (buffer-live-p buf)
292 (with-current-buffer buf
293 (if (and eshell-mode
294 eshell-history-file-name
295 eshell-ask-to-save-history
296 (or (eq eshell-ask-to-save-history 'always)
297 (y-or-n-p
298 (format "Save input history for Eshell buffer `%s'? "
299 (buffer-name buf)))))
300 (eshell-write-history))))))
301
302 (defun eshell/history (&rest args)
303 "List in help buffer the buffer's input history."
304 (eshell-init-print-buffer)
305 (eshell-eval-using-options
306 "history" args
307 '((?r "read" nil read-history
308 "read from history file to current history list")
309 (?w "write" nil write-history
310 "write current history list to history file")
311 (?a "append" nil append-history
312 "append current history list to history file")
313 (?h "help" nil nil "display this usage message")
314 :usage "[n] [-rwa [filename]]"
315 :post-usage
316 "When Eshell is started, history is read from `eshell-history-file-name'.
317 This is also the location where history info will be saved by this command,
318 unless a different file is specified on the command line.")
319 (and (or (not (ring-p eshell-history-ring))
320 (ring-empty-p eshell-history-ring))
321 (error "No history"))
322 (let (length command file)
323 (when (and args (string-match "^[0-9]+$" (car args)))
324 (setq length (min (eshell-convert (car args))
325 (ring-length eshell-history-ring))
326 args (cdr args)))
327 (and length
328 (or read-history write-history append-history)
329 (error "history: extra arguments"))
330 (when (and args (stringp (car args)))
331 (setq file (car args)
332 args (cdr args)))
333 (cond
334 (read-history (eshell-read-history file))
335 (write-history (eshell-write-history file))
336 (append-history (eshell-write-history file t))
337 (t
338 (let* ((history nil)
339 (index (1- (or length (ring-length eshell-history-ring))))
340 (ref (- (ring-length eshell-history-ring) index)))
341 ;; We have to build up a list ourselves from the ring vector.
342 (while (>= index 0)
343 (eshell-buffered-print
344 (format "%5d %s\n" ref (eshell-get-history index)))
345 (setq index (1- index)
346 ref (1+ ref)))))))
347 (eshell-flush)
348 nil))
349
350 (defun eshell-put-history (input &optional ring at-beginning)
351 "Put a new input line into the history ring."
352 (unless ring (setq ring eshell-history-ring))
353 (subst-char-in-string ?\n ?\177 input t)
354 (if at-beginning
355 (ring-insert-at-beginning ring input)
356 (ring-insert ring input)))
357
358 (defun eshell-get-history (index &optional ring)
359 "Get an input line from the history ring."
360 (unless ring (setq ring eshell-history-ring))
361 (let ((input (concat (ring-ref ring index))))
362 (subst-char-in-string ?\177 ?\n input t)
363 input))
364
365 (defun eshell-add-to-history ()
366 "Add INPUT to the history ring.
367 The input is entered into the input history ring, if the value of
368 variable `eshell-input-filter' returns non-nil when called on the
369 input."
370 (when (> (1- eshell-last-input-end) eshell-last-input-start)
371 (let ((input (buffer-substring eshell-last-input-start
372 (1- eshell-last-input-end))))
373 (if (and (funcall eshell-input-filter input)
374 (or (null eshell-hist-ignoredups)
375 (not (ring-p eshell-history-ring))
376 (ring-empty-p eshell-history-ring)
377 (not (string-equal (eshell-get-history 0) input))))
378 (eshell-put-history input))
379 (setq eshell-save-history-index eshell-history-ring)
380 (setq eshell-history-index nil))))
381
382 (defun eshell-read-history (&optional filename silent)
383 "Sets the buffer's `eshell-history-ring' from a history file.
384 The name of the file is given by the variable
385 `eshell-history-file-name'. The history ring is of size
386 `eshell-history-size', regardless of file size. If
387 `eshell-history-file-name' is nil this function does nothing.
388
389 If the optional argument SILENT is non-nil, we say nothing about a
390 failure to read the history file.
391
392 This function is useful for major mode commands and mode hooks.
393
394 The structure of the history file should be one input command per
395 line, with the most recent command last. See also
396 `eshell-hist-ignoredups' and `eshell-write-history'."
397 (let ((file (or filename eshell-history-file-name)))
398 (cond
399 ((or (null file)
400 (equal file ""))
401 nil)
402 ((not (file-readable-p file))
403 (or silent
404 (message "Cannot read history file %s" file)))
405 (t
406 (let* ((count 0)
407 (size eshell-history-size)
408 (ring (make-ring size))
409 (ignore-dups eshell-hist-ignoredups))
410 (with-temp-buffer
411 (insert-file-contents file)
412 ;; Save restriction in case file is already visited...
413 ;; Watch for those date stamps in history files!
414 (goto-char (point-max))
415 (while (and (< count size)
416 (re-search-backward "^[ \t]*\\([^#\n].*\\)[ \t]*$"
417 nil t))
418 (let ((history (match-string 1)))
419 (if (or (null ignore-dups)
420 (ring-empty-p ring)
421 (not (string-equal (ring-ref ring 0) history)))
422 (ring-insert-at-beginning ring history)))
423 (setq count (1+ count))))
424 (setq eshell-history-ring ring
425 eshell-history-index nil))))))
426
427 (defun eshell-write-history (&optional filename append)
428 "Writes the buffer's `eshell-history-ring' to a history file.
429 The name of the file is given by the variable
430 `eshell-history-file-name'. The original contents of the file are
431 lost if `eshell-history-ring' is not empty. If
432 `eshell-history-file-name' is nil this function does nothing.
433
434 Useful within process sentinels.
435
436 See also `eshell-read-history'."
437 (let ((file (or filename eshell-history-file-name)))
438 (cond
439 ((or (null file)
440 (equal file "")
441 (null eshell-history-ring)
442 (ring-empty-p eshell-history-ring))
443 nil)
444 ((not (file-writable-p file))
445 (message "Cannot write history file %s" file))
446 (t
447 (let* ((ring eshell-history-ring)
448 (index (ring-length ring)))
449 ;; Write it all out into a buffer first. Much faster, but
450 ;; messier, than writing it one line at a time.
451 (with-temp-buffer
452 (while (> index 0)
453 (setq index (1- index))
454 (insert (ring-ref ring index) ?\n))
455 (eshell-with-private-file-modes
456 (write-region (point-min) (point-max) file append
457 'no-message))))))))
458
459 (defun eshell-list-history ()
460 "List in help buffer the buffer's input history."
461 (interactive)
462 (let (prefix prelen)
463 (save-excursion
464 (if (re-search-backward "!\\(.+\\)" (line-beginning-position) t)
465 (setq prefix (match-string 1)
466 prelen (length prefix))))
467 (if (or (not (ring-p eshell-history-ring))
468 (ring-empty-p eshell-history-ring))
469 (message "No history")
470 (let ((history nil)
471 (history-buffer " *Input History*")
472 (index (1- (ring-length eshell-history-ring)))
473 (conf (current-window-configuration)))
474 ;; We have to build up a list ourselves from the ring vector.
475 (while (>= index 0)
476 (let ((hist (eshell-get-history index)))
477 (if (or (not prefix)
478 (and (>= (length hist) prelen)
479 (string= (substring hist 0 prelen) prefix)))
480 (setq history (cons hist history))))
481 (setq index (1- index)))
482 ;; Change "completion" to "history reference"
483 ;; to make the display accurate.
484 (with-output-to-temp-buffer history-buffer
485 (display-completion-list history)
486 (set-buffer history-buffer)
487 (forward-line 3)
488 (while (search-backward "completion" nil 'move)
489 (replace-match "history reference")))
490 (eshell-redisplay)
491 (message "Hit space to flush")
492 (let ((ch (read-event)))
493 (if (eq ch ?\ )
494 (set-window-configuration conf)
495 (setq unread-command-events (list ch))))))))
496
497 (defun eshell-hist-word-reference (ref)
498 "Return the word designator index referred to by REF."
499 (cond
500 ((string-match "^[0-9]+$" ref)
501 (string-to-number ref))
502 ((string= "^" ref) 1)
503 ((string= "$" ref) nil)
504 ((string= "%" ref)
505 (error "`%' history word designator not yet implemented"))))
506
507 (defun eshell-hist-parse-arguments (&optional silent b e)
508 "Parse current command arguments in a history-code-friendly way."
509 (let ((end (or e (point)))
510 (begin (or b (save-excursion (eshell-bol) (point))))
511 (posb (list t))
512 (pose (list t))
513 (textargs (list t))
514 hist args)
515 (unless (catch 'eshell-incomplete
516 (ignore
517 (setq args (eshell-parse-arguments begin end))))
518 (save-excursion
519 (goto-char begin)
520 (while (< (point) end)
521 (if (get-text-property (point) 'arg-begin)
522 (nconc posb (list (point))))
523 (if (get-text-property (point) 'arg-end)
524 (nconc pose
525 (list (if (= (1+ (point)) end)
526 (1+ (point))
527 (point)))))
528 (forward-char))
529 (setq posb (cdr posb)
530 pose (cdr pose))
531 (assert (= (length posb) (length args)))
532 (assert (<= (length posb) (length pose))))
533 (setq hist (buffer-substring-no-properties begin end))
534 (let ((b posb) (e pose))
535 (while b
536 (nconc textargs
537 (list (substring hist (- (car b) begin)
538 (- (car e) begin))))
539 (setq b (cdr b)
540 e (cdr e))))
541 (setq textargs (cdr textargs))
542 (assert (= (length textargs) (length args)))
543 (list textargs posb pose))))
544
545 (defun eshell-expand-history-references (beg end)
546 "Parse and expand any history references in current input."
547 (let ((result (eshell-hist-parse-arguments t beg end)))
548 (when result
549 (let ((textargs (nreverse (nth 0 result)))
550 (posb (nreverse (nth 1 result)))
551 (pose (nreverse (nth 2 result))))
552 (save-excursion
553 (while textargs
554 (let ((str (eshell-history-reference (car textargs))))
555 (unless (eq str (car textargs))
556 (goto-char (car posb))
557 (insert-and-inherit str)
558 (delete-char (- (car pose) (car posb)))))
559 (setq textargs (cdr textargs)
560 posb (cdr posb)
561 pose (cdr pose))))))))
562
563 (defun eshell-complete-history-reference ()
564 "Complete a history reference, by completing the event designator."
565 (let ((arg (pcomplete-actual-arg)))
566 (when (string-match "\\`![^:^$*%]*\\'" arg)
567 (setq pcomplete-stub (substring arg 1)
568 pcomplete-last-completion-raw t)
569 (throw 'pcomplete-completions
570 (let ((history nil)
571 (index (1- (ring-length eshell-history-ring)))
572 (stublen (length pcomplete-stub)))
573 ;; We have to build up a list ourselves from the ring
574 ;; vector.
575 (while (>= index 0)
576 (let ((hist (eshell-get-history index)))
577 (if (and (>= (length hist) stublen)
578 (string= (substring hist 0 stublen)
579 pcomplete-stub)
580 (string-match "^\\([^:^$*% \t\n]+\\)" hist))
581 (setq history (cons (match-string 1 hist)
582 history))))
583 (setq index (1- index)))
584 (let ((fhist (list t)))
585 ;; uniqify the list, but preserve the order
586 (while history
587 (unless (member (car history) fhist)
588 (nconc fhist (list (car history))))
589 (setq history (cdr history)))
590 (cdr fhist)))))))
591
592 (defun eshell-history-reference (reference)
593 "Expand directory stack REFERENCE.
594 The syntax used here was taken from the Bash info manual.
595 Returns the resultant reference, or the same string REFERENCE if none
596 matched."
597 ;; `^string1^string2^'
598 ;; Quick Substitution. Repeat the last command, replacing
599 ;; STRING1 with STRING2. Equivalent to `!!:s/string1/string2/'
600 (if (and (eshell-using-module 'eshell-pred)
601 (string-match "\\^\\([^^]+\\)\\^\\([^^]+\\)\\^?\\s-*$"
602 reference))
603 (setq reference (format "!!:s/%s/%s/"
604 (match-string 1 reference)
605 (match-string 2 reference))))
606 ;; `!'
607 ;; Start a history substitution, except when followed by a
608 ;; space, tab, the end of the line, = or (.
609 (if (not (string-match "^![^ \t\n=\(]" reference))
610 reference
611 (setq eshell-history-index nil)
612 (let ((event (eshell-hist-parse-event-designator reference)))
613 (unless event
614 (error "Could not find history event `%s'" reference))
615 (setq eshell-history-index (car event)
616 reference (substring reference (cdr event))
617 event (eshell-get-history eshell-history-index))
618 (if (not (string-match "^[:^$*%]" reference))
619 event
620 (let ((word (eshell-hist-parse-word-designator
621 event reference)))
622 (unless word
623 (error "Unable to honor word designator `%s'" reference))
624 (unless (string-match "^[:^$*%][[$^*%0-9-]" reference)
625 (setcdr word 0))
626 (setq event (car word)
627 reference (substring reference (cdr word)))
628 (if (not (and (eshell-using-module 'eshell-pred)
629 (string-match "^:" reference)))
630 event
631 (eshell-hist-parse-modifier event reference)))))))
632
633 (defun eshell-hist-parse-event-designator (reference)
634 "Parse a history event designator beginning in REFERENCE."
635 (let* ((index (string-match eshell-hist-event-designator reference))
636 (end (and index (match-end 0))))
637 (unless index
638 (error "Invalid history event designator `%s'" reference))
639 (let* ((event (match-string 1 reference))
640 (pos
641 (cond
642 ((string= event "!") (ring-length eshell-history-ring))
643 ((string= event "#") (error "!# not yet implemented"))
644 ((string-match "^-?[0-9]+$" event)
645 (let ((num (string-to-number event)))
646 (if (>= num 0)
647 (- (ring-length eshell-history-ring) num)
648 (1- (abs num)))))
649 ((string-match "^\\(\\??\\)\\([^?]+\\)\\??$" event)
650 (let ((pref (if (> (length (match-string 1 event)) 0)
651 "" "^"))
652 (str (match-string 2 event)))
653 (save-match-data
654 (eshell-previous-matching-input-string-position
655 (concat pref (regexp-quote str)) 1))))
656 (t
657 (error "Failed to parse event designator `%s'" event)))))
658 (and pos (cons pos end)))))
659
660 (defun eshell-hist-parse-word-designator (hist reference)
661 "Parse a history word designator beginning for HIST in REFERENCE."
662 (let* ((index (string-match eshell-hist-word-designator reference))
663 (end (and index (match-end 0))))
664 (unless (memq (aref reference 0) '(?: ?^ ?$ ?* ?%))
665 (error "Invalid history word designator `%s'" reference))
666 (let ((nth (match-string 1 reference))
667 (mth (match-string 2 reference))
668 (here (point))
669 textargs)
670 (insert hist)
671 (setq textargs (car (eshell-hist-parse-arguments nil here (point))))
672 (delete-region here (point))
673 (if (string= nth "*")
674 (if mth
675 (error "Invalid history word designator `%s'"
676 reference)
677 (setq nth 1 mth "-$")))
678 (if (not mth)
679 (if nth
680 (setq mth nth)
681 (setq nth 0 mth "$"))
682 (if (string= mth "-")
683 (setq mth (- (length textargs) 2))
684 (if (string= mth "*")
685 (setq mth "$")
686 (if (not (and (> (length mth) 1)
687 (eq (aref mth 0) ?-)))
688 (error "Invalid history word designator `%s'"
689 reference)
690 (setq mth (substring mth 1))))))
691 (unless (numberp nth)
692 (setq nth (eshell-hist-word-reference nth)))
693 (unless (numberp mth)
694 (setq mth (eshell-hist-word-reference mth)))
695 (cons (mapconcat 'identity (eshell-sublist textargs nth mth) "")
696 end))))
697
698 (defun eshell-hist-parse-modifier (hist reference)
699 "Parse a history modifier beginning for HIST in REFERENCE."
700 (let ((here (point)))
701 (insert reference)
702 (prog1
703 (save-restriction
704 (narrow-to-region here (point))
705 (goto-char (point-min))
706 (let ((modifiers (cdr (eshell-parse-modifiers))))
707 (eshell-for mod modifiers
708 (setq hist (funcall mod hist)))
709 hist))
710 (delete-region here (point)))))
711
712 (defun eshell-get-next-from-history ()
713 "After fetching a line from input history, this fetches the next.
714 In other words, this recalls the input line after the line you
715 recalled last. You can use this to repeat a sequence of input lines."
716 (interactive)
717 (if eshell-save-history-index
718 (progn
719 (setq eshell-history-index (1+ eshell-save-history-index))
720 (eshell-next-input 1))
721 (message "No previous history command")))
722
723 (defun eshell-search-arg (arg)
724 ;; First make sure there is a ring and that we are after the process
725 ;; mark
726 (if (and eshell-hist-move-to-end
727 (< (point) eshell-last-output-end))
728 (goto-char eshell-last-output-end))
729 (cond ((or (null eshell-history-ring)
730 (ring-empty-p eshell-history-ring))
731 (error "Empty input ring"))
732 ((zerop arg)
733 ;; arg of zero resets search from beginning, and uses arg of
734 ;; 1
735 (setq eshell-history-index nil)
736 1)
737 (t
738 arg)))
739
740 (defun eshell-search-start (arg)
741 "Index to start a directional search, starting at `eshell-history-index'."
742 (if eshell-history-index
743 ;; If a search is running, offset by 1 in direction of arg
744 (mod (+ eshell-history-index (if (> arg 0) 1 -1))
745 (ring-length eshell-history-ring))
746 ;; For a new search, start from beginning or end, as appropriate
747 (if (>= arg 0)
748 0 ; First elt for forward search
749 ;; Last elt for backward search
750 (1- (ring-length eshell-history-ring)))))
751
752 (defun eshell-previous-input-string (arg)
753 "Return the string ARG places along the input ring.
754 Moves relative to `eshell-history-index'."
755 (eshell-get-history (if eshell-history-index
756 (mod (+ arg eshell-history-index)
757 (ring-length eshell-history-ring))
758 arg)))
759
760 (defun eshell-previous-input (arg)
761 "Cycle backwards through input history."
762 (interactive "*p")
763 (eshell-previous-matching-input "." arg))
764
765 (defun eshell-next-input (arg)
766 "Cycle forwards through input history."
767 (interactive "*p")
768 (eshell-previous-input (- arg)))
769
770 (defun eshell-previous-matching-input-string (regexp arg)
771 "Return the string matching REGEXP ARG places along the input ring.
772 Moves relative to `eshell-history-index'."
773 (let* ((pos (eshell-previous-matching-input-string-position regexp arg)))
774 (if pos (eshell-get-history pos))))
775
776 (defun eshell-previous-matching-input-string-position
777 (regexp arg &optional start)
778 "Return the index matching REGEXP ARG places along the input ring.
779 Moves relative to START, or `eshell-history-index'."
780 (if (or (not (ring-p eshell-history-ring))
781 (ring-empty-p eshell-history-ring))
782 (error "No history"))
783 (let* ((len (ring-length eshell-history-ring))
784 (motion (if (> arg 0) 1 -1))
785 (n (mod (- (or start (eshell-search-start arg)) motion) len))
786 (tried-each-ring-item nil)
787 (case-fold-search (eshell-under-windows-p))
788 (prev nil))
789 ;; Do the whole search as many times as the argument says.
790 (while (and (/= arg 0) (not tried-each-ring-item))
791 ;; Step once.
792 (setq prev n
793 n (mod (+ n motion) len))
794 ;; If we haven't reached a match, step some more.
795 (while (and (< n len) (not tried-each-ring-item)
796 (not (string-match regexp (eshell-get-history n))))
797 (setq n (mod (+ n motion) len)
798 ;; If we have gone all the way around in this search.
799 tried-each-ring-item (= n prev)))
800 (setq arg (if (> arg 0) (1- arg) (1+ arg))))
801 ;; Now that we know which ring element to use, if we found it,
802 ;; return that.
803 (if (string-match regexp (eshell-get-history n))
804 n)))
805
806 (defun eshell-previous-matching-input (regexp arg)
807 "Search backwards through input history for match for REGEXP.
808 \(Previous history elements are earlier commands.)
809 With prefix argument N, search for Nth previous match.
810 If N is negative, find the next or Nth next match."
811 (interactive (eshell-regexp-arg "Previous input matching (regexp): "))
812 (setq arg (eshell-search-arg arg))
813 (let ((pos (eshell-previous-matching-input-string-position regexp arg)))
814 ;; Has a match been found?
815 (if (null pos)
816 (error "Not found")
817 (setq eshell-history-index pos)
818 (message "History item: %d" (- (ring-length eshell-history-ring) pos))
819 ;; Can't use kill-region as it sets this-command
820 (delete-region (save-excursion (eshell-bol) (point)) (point))
821 (insert-and-inherit (eshell-get-history pos)))))
822
823 (defun eshell-next-matching-input (regexp arg)
824 "Search forwards through input history for match for REGEXP.
825 \(Later history elements are more recent commands.)
826 With prefix argument N, search for Nth following match.
827 If N is negative, find the previous or Nth previous match."
828 (interactive (eshell-regexp-arg "Next input matching (regexp): "))
829 (eshell-previous-matching-input regexp (- arg)))
830
831 (defun eshell-previous-matching-input-from-input (arg)
832 "Search backwards through input history for match for current input.
833 \(Previous history elements are earlier commands.)
834 With prefix argument N, search for Nth previous match.
835 If N is negative, search forwards for the -Nth following match."
836 (interactive "p")
837 (if (not (memq last-command '(eshell-previous-matching-input-from-input
838 eshell-next-matching-input-from-input)))
839 ;; Starting a new search
840 (setq eshell-matching-input-from-input-string
841 (buffer-substring (save-excursion (eshell-bol) (point))
842 (point))
843 eshell-history-index nil))
844 (eshell-previous-matching-input
845 (concat "^" (regexp-quote eshell-matching-input-from-input-string))
846 arg))
847
848 (defun eshell-next-matching-input-from-input (arg)
849 "Search forwards through input history for match for current input.
850 \(Following history elements are more recent commands.)
851 With prefix argument N, search for Nth following match.
852 If N is negative, search backwards for the -Nth previous match."
853 (interactive "p")
854 (eshell-previous-matching-input-from-input (- arg)))
855
856 (defun eshell-test-imatch ()
857 "If isearch match good, put point at the beginning and return non-nil."
858 (if (get-text-property (point) 'history)
859 (progn (beginning-of-line) t)
860 (let ((before (point)))
861 (eshell-bol)
862 (if (and (not (bolp))
863 (<= (point) before))
864 t
865 (if isearch-forward
866 (progn
867 (end-of-line)
868 (forward-char))
869 (beginning-of-line)
870 (backward-char))))))
871
872 (defun eshell-return-to-prompt ()
873 "Once a search string matches, insert it at the end and go there."
874 (setq isearch-other-end nil)
875 (let ((found (eshell-test-imatch)) before)
876 (while (and (not found)
877 (setq before
878 (funcall (if isearch-forward
879 're-search-forward
880 're-search-backward)
881 isearch-string nil t)))
882 (setq found (eshell-test-imatch)))
883 (if (not found)
884 (progn
885 (goto-char eshell-last-output-end)
886 (delete-region (point) (point-max)))
887 (setq before (point))
888 (let ((text (buffer-substring-no-properties
889 (point) (line-end-position)))
890 (orig (marker-position eshell-last-output-end)))
891 (goto-char eshell-last-output-end)
892 (delete-region (point) (point-max))
893 (when (and text (> (length text) 0))
894 (subst-char-in-string ?\177 ?\n text t)
895 (insert text)
896 (put-text-property (1- (point)) (point)
897 'last-search-pos before)
898 (set-marker eshell-last-output-end orig)
899 (goto-char eshell-last-output-end))))))
900
901 (defun eshell-prepare-for-search ()
902 "Make sure the old history file is at the beginning of the buffer."
903 (unless (get-text-property (point-min) 'history)
904 (save-excursion
905 (goto-char (point-min))
906 (let ((end (copy-marker (point) t)))
907 (insert-file-contents eshell-history-file-name)
908 (set-text-properties (point-min) end
909 '(history t invisible t))))))
910
911 (defun eshell-isearch-backward (&optional invert)
912 "Do incremental regexp search backward through past commands."
913 (interactive)
914 (let ((inhibit-read-only t) end)
915 (eshell-prepare-for-search)
916 (goto-char (point-max))
917 (set-marker eshell-last-output-end (point))
918 (delete-region (point) (point-max)))
919 (isearch-mode invert t 'eshell-return-to-prompt))
920
921 (defun eshell-isearch-repeat-backward (&optional invert)
922 "Do incremental regexp search backward through past commands."
923 (interactive)
924 (let ((old-pos (get-text-property (1- (point-max))
925 'last-search-pos)))
926 (when old-pos
927 (goto-char old-pos)
928 (if invert
929 (end-of-line)
930 (backward-char)))
931 (setq isearch-forward invert)
932 (isearch-search-and-update)))
933
934 (defun eshell-isearch-forward ()
935 "Do incremental regexp search backward through past commands."
936 (interactive)
937 (eshell-isearch-backward t))
938
939 (defun eshell-isearch-repeat-forward ()
940 "Do incremental regexp search backward through past commands."
941 (interactive)
942 (eshell-isearch-repeat-backward t))
943
944 (defun eshell-isearch-cancel ()
945 (interactive)
946 (goto-char eshell-last-output-end)
947 (delete-region (point) (point-max))
948 (call-interactively 'isearch-cancel))
949
950 (defun eshell-isearch-abort ()
951 (interactive)
952 (goto-char eshell-last-output-end)
953 (delete-region (point) (point-max))
954 (call-interactively 'isearch-abort))
955
956 (defun eshell-isearch-delete-char ()
957 (interactive)
958 (save-excursion
959 (isearch-delete-char)))
960
961 (defun eshell-isearch-return ()
962 (interactive)
963 (isearch-done)
964 (eshell-send-input))
965
966 ;;; em-hist.el ends here