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