diff lisp/eshell/em-hist.el @ 29876:edfec1c0d511

*** empty log message ***
author Gerd Moellmann <gerd@gnu.org>
date Fri, 23 Jun 2000 05:24:10 +0000
parents
children 34b1ab9d583d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/eshell/em-hist.el	Fri Jun 23 05:24:10 2000 +0000
@@ -0,0 +1,966 @@
+;;; em-hist --- history list management
+
+;; Copyright (C) 1999, 2000 Free Sofware Foundation
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+(provide 'em-hist)
+
+(eval-when-compile (require 'esh-maint))
+
+(defgroup eshell-hist nil
+  "This module provides command history management."
+  :tag "History list management"
+  :group 'eshell-module)
+
+;;; Commentary:
+
+;; Eshell's history facility imitates the syntax used by bash
+;; ([(bash)History Interaction]).  Thus:
+;;
+;;   !ls           ; repeat the last command beginning with 'ls'
+;;   !?ls          ; repeat the last command containing ls
+;;   echo !ls:2    ; echo the second arg of the last 'ls' command
+;;   !ls<tab>      ; complete against all possible words in this
+;;                 ; position, by looking at the history list
+;;   !ls<C-c SPC>  ; expand any matching history input at point
+;;
+;; Also, most of `comint-mode's keybindings are accepted:
+;;
+;;   M-r     ; search backward for a previous command by regexp
+;;   M-s     ; search forward for a previous command by regexp
+;;   M-p     ; access the last command entered, repeatable
+;;   M-n     ; access the first command entered, repeatable
+;;
+;;   C-c M-r ; using current input, find a matching command thus, with
+;;           ; 'ls' as the current input, it will go back to the same
+;;           ; command that '!ls' would have selected
+;;   C-c M-s ; same, but in reverse order
+;;
+;; Note that some of these keybindings are only available if the
+;; `eshell-rebind' is not in use, in which case M-p does what C-c M-r
+;; normally would do, and C-p is used instead of M-p.  It may seem
+;; confusing, but the intention is to make the most useful
+;; functionality the most easily accessible.  If `eshell-rebind' is
+;; not being used, history navigation will use comint's keybindings;
+;; if it is, history navigation tries to use similar keybindings to
+;; bash.  This is all configurable, of course.
+
+;;; Code:
+
+(require 'ring)
+(require 'esh-opt)
+(require 'em-pred)
+
+;;; User Variables:
+
+(defcustom eshell-hist-load-hook '(eshell-hist-initialize)
+  "*A list of functions to call when loading `eshell-hist'."
+  :type 'hook
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-unload-hook
+  (list
+   (function
+    (lambda ()
+      (remove-hook 'kill-emacs-hook 'eshell-save-some-history))))
+  "*A hook that gets run when `eshell-hist' is unloaded."
+  :type 'hook
+  :group 'eshell-hist)
+
+(defcustom eshell-history-file-name
+  (concat eshell-directory-name "history")
+  "*If non-nil, name of the file to read/write input history.
+See also `eshell-read-history' and `eshell-write-history'.
+If it is nil, Eshell will use the value of HISTFILE."
+  :type 'file
+  :group 'eshell-hist)
+
+(defcustom eshell-history-size 128
+  "*Size of the input history ring.  If nil, use envvar HISTSIZE."
+  :type 'integer
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-ignoredups nil
+  "*If non-nil, don't add input matching the last on the input ring.
+This mirrors the optional behavior of bash."
+  :type 'boolean
+  :group 'eshell-hist)
+
+(defcustom eshell-ask-to-save-history t
+  "*Determine if history should be automatically saved.
+History is always preserved after sanely exiting an Eshell buffer.
+However, when Emacs is being shut down, this variable determines
+whether to prompt the user.
+If set to nil, it means never ask whether history should be saved.
+If set to t, always ask if any Eshell buffers are open at exit time.
+If set to `always', history will always be saved, silently."
+  :type '(choice (const :tag "Never" nil)
+		 (const :tag "Ask" t)
+		 (const :tag "Always save" always))
+  :group 'eshell-hist)
+
+(defcustom eshell-input-filter
+  (function
+   (lambda (str)
+     (not (string-match "\\`\\s-*\\'" str))))
+  "*Predicate for filtering additions to input history.
+Takes one argument, the input.  If non-nil, the input may be saved on
+the input history list.  Default is to save anything that isn't all
+whitespace."
+  :type 'function
+  :group 'eshell-hist)
+
+(put 'eshell-input-filter 'risky-local-variable t)
+
+(defcustom eshell-hist-match-partial t
+  "*If non-nil, movement through history is constrained by current input.
+Otherwise, typing <M-p> and <M-n> will always go to the next history
+element, regardless of any text on the command line.  In that case,
+<C-c M-r> and <C-c M-s> still offer that functionality."
+  :type 'boolean
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-move-to-end t
+  "*If non-nil, move to the end of the buffer before cycling history."
+  :type 'boolean
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-event-designator
+  "^!\\(!\\|-?[0-9]+\\|\\??[^:^$%*?]+\\??\\|#\\)"
+  "*The regexp used to identifier history event designators."
+  :type 'regexp
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-word-designator
+  "^:?\\([0-9]+\\|[$^%*]\\)?\\(\\*\\|-[0-9]*\\|[$^%*]\\)?"
+  "*The regexp used to identify history word designators."
+  :type 'regexp
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-modifier
+  "^\\(:\\([hretpqx&g]\\|s/\\([^/]*\\)/\\([^/]*\\)/\\)\\)*"
+  "*The regexp used to identity history modifiers."
+  :type 'regexp
+  :group 'eshell-hist)
+
+(defcustom eshell-hist-rebind-keys-alist
+  '(([(control ?p)]   . eshell-previous-input)
+    ([(control ?n)]   . eshell-next-input)
+    ([(control up)]   . eshell-previous-input)
+    ([(control down)] . eshell-next-input)
+    ([(control ?r)]   . eshell-isearch-backward)
+    ([(control ?s)]   . eshell-isearch-forward)
+    ([(meta ?r)]      . eshell-previous-matching-input)
+    ([(meta ?s)]      . eshell-next-matching-input)
+    ([(meta ?p)]      . eshell-previous-matching-input-from-input)
+    ([(meta ?n)]      . eshell-next-matching-input-from-input)
+    ([up]             . eshell-previous-matching-input-from-input)
+    ([down]           . eshell-next-matching-input-from-input))
+  "*History keys to bind differently if point is in input text."
+  :type '(repeat (cons (vector :tag "Keys to bind"
+			       (repeat :inline t sexp))
+		       (function :tag "Command")))
+  :group 'eshell-hist)
+
+;;; Internal Variables:
+
+(defvar eshell-history-ring nil)
+(defvar eshell-history-index nil)
+(defvar eshell-matching-input-from-input-string "")
+(defvar eshell-save-history-index nil)
+
+(defvar eshell-isearch-map nil)
+
+(unless eshell-isearch-map
+  (setq eshell-isearch-map (copy-keymap isearch-mode-map))
+  (define-key eshell-isearch-map [(control ?m)] 'eshell-isearch-return)
+  (define-key eshell-isearch-map [return] 'eshell-isearch-return)
+  (define-key eshell-isearch-map [(control ?r)] 'eshell-isearch-repeat-backward)
+  (define-key eshell-isearch-map [(control ?s)] 'eshell-isearch-repeat-forward)
+  (define-key eshell-isearch-map [(control ?g)] 'eshell-isearch-abort)
+  (define-key eshell-isearch-map [backspace] 'eshell-isearch-delete-char)
+  (define-key eshell-isearch-map [delete] 'eshell-isearch-delete-char)
+  (defvar eshell-isearch-cancel-map)
+  (define-prefix-command 'eshell-isearch-cancel-map)
+  (define-key eshell-isearch-map [(control ?c)] 'eshell-isearch-cancel-map)
+  (define-key eshell-isearch-cancel-map [(control ?c)] 'eshell-isearch-cancel))
+
+;;; Functions:
+
+(defun eshell-hist-initialize ()
+  "Initialize the history management code for one Eshell buffer."
+  (make-local-hook 'eshell-expand-input-functions)
+  (add-hook 'eshell-expand-input-functions
+	    'eshell-expand-history-references nil t)
+
+  (when (eshell-using-module 'eshell-cmpl)
+    (make-local-hook 'pcomplete-try-first-hook)
+    (add-hook 'pcomplete-try-first-hook
+	      'eshell-complete-history-reference nil t))
+
+  (if (eshell-using-module 'eshell-rebind)
+      (let ((rebind-alist (symbol-value 'eshell-rebind-keys-alist)))
+	(make-local-variable 'eshell-rebind-keys-alist)
+	(set 'eshell-rebind-keys-alist
+	     (append rebind-alist eshell-hist-rebind-keys-alist))
+	(set (make-local-variable 'search-invisible) t)
+	(set (make-local-variable 'search-exit-option) t)
+	(make-local-hook 'isearch-mode-hook)
+	(add-hook 'isearch-mode-hook
+		  (function
+		   (lambda ()
+		     (if (>= (point) eshell-last-output-end)
+			 (setq overriding-terminal-local-map
+			       eshell-isearch-map)))) nil t)
+	(make-local-hook 'isearch-mode-end-hook)
+	(add-hook 'isearch-mode-end-hook
+		  (function
+		   (lambda ()
+		     (setq overriding-terminal-local-map nil))) nil t))
+    (define-key eshell-mode-map [up] 'eshell-previous-matching-input-from-input)
+    (define-key eshell-mode-map [down] 'eshell-next-matching-input-from-input)
+    (define-key eshell-mode-map [(control up)] 'eshell-previous-input)
+    (define-key eshell-mode-map [(control down)] 'eshell-next-input)
+    (define-key eshell-mode-map [(meta ?r)] 'eshell-previous-matching-input)
+    (define-key eshell-mode-map [(meta ?s)] 'eshell-next-matching-input)
+    (define-key eshell-command-map [(meta ?r)]
+      'eshell-previous-matching-input-from-input)
+    (define-key eshell-command-map [(meta ?s)]
+      'eshell-next-matching-input-from-input)
+    (if eshell-hist-match-partial
+	(progn
+	  (define-key eshell-mode-map [(meta ?p)]
+	    'eshell-previous-matching-input-from-input)
+	  (define-key eshell-mode-map [(meta ?n)]
+	    'eshell-next-matching-input-from-input)
+	  (define-key eshell-command-map [(meta ?p)] 'eshell-previous-input)
+	  (define-key eshell-command-map [(meta ?n)] 'eshell-next-input))
+      (define-key eshell-mode-map [(meta ?p)] 'eshell-previous-input)
+      (define-key eshell-mode-map [(meta ?n)] 'eshell-next-input)
+      (define-key eshell-command-map [(meta ?p)]
+	'eshell-previous-matching-input-from-input)
+      (define-key eshell-command-map [(meta ?n)]
+	'eshell-next-matching-input-from-input)))
+
+  (make-local-variable 'eshell-history-size)
+  (or eshell-history-size
+      (setq eshell-history-size (getenv "HISTSIZE")))
+
+  (make-local-variable 'eshell-history-file-name)
+  (or eshell-history-file-name
+      (setq eshell-history-file-name (getenv "HISTFILE")))
+
+  (make-local-variable 'eshell-history-index)
+  (make-local-variable 'eshell-save-history-index)
+  (make-local-variable 'eshell-history-ring)
+  (if eshell-history-file-name
+      (eshell-read-history nil t))
+  (unless eshell-history-ring
+    (setq eshell-history-ring (make-ring eshell-history-size)))
+
+  (make-local-hook 'eshell-exit-hook)
+  (add-hook 'eshell-exit-hook 'eshell-write-history nil t)
+
+  (add-hook 'kill-emacs-hook 'eshell-save-some-history)
+
+  (make-local-variable 'eshell-input-filter-functions)
+  (add-hook 'eshell-input-filter-functions 'eshell-add-to-history nil t)
+
+  (define-key eshell-command-map [(control ?l)] 'eshell-list-history)
+  (define-key eshell-command-map [(control ?x)] 'eshell-get-next-from-history))
+
+(defun eshell-save-some-history ()
+  "Save the history for any open Eshell buffers."
+  (eshell-for buf (buffer-list)
+    (if (buffer-live-p buf)
+	(with-current-buffer buf
+	  (if (and eshell-mode
+		   eshell-history-file-name
+		   eshell-ask-to-save-history
+		   (or (eq eshell-ask-to-save-history 'always)
+		       (y-or-n-p
+			(format "Save input history for Eshell buffer `%s'? "
+				(buffer-name buf)))))
+	      (eshell-write-history))))))
+
+(defun eshell/history (&rest args)
+  "List in help buffer the buffer's input history."
+  (eshell-init-print-buffer)
+  (eshell-eval-using-options
+   "history" args
+   '((?r "read" nil read-history
+	 "read from history file to current history list")
+     (?w "write" nil write-history
+	 "write current history list to history file")
+     (?a "append" nil append-history
+	 "append current history list to history file")
+     (?h "help" nil nil "display this usage message")
+     :usage "[n] [-rwa [filename]]"
+     :post-usage
+"When Eshell is started, history is read from `eshell-history-file-name'.
+This is also the location where history info will be saved by this command,
+unless a different file is specified on the command line.")
+   (and (or (not (ring-p eshell-history-ring))
+	   (ring-empty-p eshell-history-ring))
+	(error "No history"))
+   (let (length command file)
+     (when (and args (string-match "^[0-9]+$" (car args)))
+       (setq length (min (eshell-convert (car args))
+			 (ring-length eshell-history-ring))
+	     args (cdr args)))
+     (and length
+	  (or read-history write-history append-history)
+	  (error "history: extra arguments"))
+     (when (and args (stringp (car args)))
+       (setq file (car args)
+	     args (cdr args)))
+     (cond
+      (read-history (eshell-read-history file))
+      (write-history (eshell-write-history file))
+      (append-history (eshell-write-history file t))
+      (t
+       (let* ((history nil)
+	      (index (1- (or length (ring-length eshell-history-ring))))
+	      (ref (- (ring-length eshell-history-ring) index)))
+	 ;; We have to build up a list ourselves from the ring vector.
+	 (while (>= index 0)
+	   (eshell-buffered-print
+	    (format "%5d  %s\n" ref (eshell-get-history index)))
+	   (setq index (1- index)
+		 ref (1+ ref)))))))
+   (eshell-flush)
+   nil))
+
+(defun eshell-put-history (input &optional ring at-beginning)
+  "Put a new input line into the history ring."
+  (unless ring (setq ring eshell-history-ring))
+  (subst-char-in-string ?\n ?\177 input t)
+  (if at-beginning
+      (ring-insert-at-beginning ring input)
+    (ring-insert ring input)))
+
+(defun eshell-get-history (index &optional ring)
+  "Get an input line from the history ring."
+  (unless ring (setq ring eshell-history-ring))
+  (let ((input (concat (ring-ref ring index))))
+    (subst-char-in-string ?\177 ?\n input t)
+    input))
+
+(defun eshell-add-to-history ()
+  "Add INPUT to the history ring.
+The input is entered into the input history ring, if the value of
+variable `eshell-input-filter' returns non-nil when called on the
+input."
+  (when (> (1- eshell-last-input-end) eshell-last-input-start)
+    (let ((input (buffer-substring eshell-last-input-start
+				   (1- eshell-last-input-end))))
+      (if (and (funcall eshell-input-filter input)
+	       (or (null eshell-hist-ignoredups)
+		   (not (ring-p eshell-history-ring))
+		   (ring-empty-p eshell-history-ring)
+		   (not (string-equal (eshell-get-history 0) input))))
+	  (eshell-put-history input))
+      (setq eshell-save-history-index eshell-history-ring)
+      (setq eshell-history-index nil))))
+
+(defun eshell-read-history (&optional filename silent)
+  "Sets the buffer's `eshell-history-ring' from a history file.
+The name of the file is given by the variable
+`eshell-history-file-name'.  The history ring is of size
+`eshell-history-size', regardless of file size.  If
+`eshell-history-file-name' is nil this function does nothing.
+
+If the optional argument SILENT is non-nil, we say nothing about a
+failure to read the history file.
+
+This function is useful for major mode commands and mode hooks.
+
+The structure of the history file should be one input command per
+line, with the most recent command last.  See also
+`eshell-hist-ignoredups' and `eshell-write-history'."
+  (let ((file (or filename eshell-history-file-name)))
+    (cond
+     ((or (null file)
+	  (equal file ""))
+      nil)
+     ((not (file-readable-p file))
+      (or silent
+	  (message "Cannot read history file %s" file)))
+     (t
+      (let* ((count 0)
+	     (size eshell-history-size)
+	     (ring (make-ring size))
+	     (ignore-dups eshell-hist-ignoredups))
+	(with-temp-buffer
+	  (insert-file-contents file)
+	  ;; Save restriction in case file is already visited...
+	  ;; Watch for those date stamps in history files!
+	  (goto-char (point-max))
+	  (while (and (< count size)
+		      (re-search-backward "^[ \t]*\\([^#\n].*\\)[ \t]*$"
+					  nil t))
+	    (let ((history (match-string 1)))
+	      (if (or (null ignore-dups)
+		      (ring-empty-p ring)
+		      (not (string-equal (ring-ref ring 0) history)))
+		  (ring-insert-at-beginning ring history)))
+	    (setq count (1+ count))))
+	(setq eshell-history-ring ring
+	      eshell-history-index nil))))))
+
+(defun eshell-write-history (&optional filename append)
+  "Writes the buffer's `eshell-history-ring' to a history file.
+The name of the file is given by the variable
+`eshell-history-file-name'.  The original contents of the file are
+lost if `eshell-history-ring' is not empty.  If
+`eshell-history-file-name' is nil this function does nothing.
+
+Useful within process sentinels.
+
+See also `eshell-read-history'."
+  (let ((file (or filename eshell-history-file-name)))
+    (cond
+     ((or (null file)
+	  (equal file "")
+	  (null eshell-history-ring)
+	  (ring-empty-p eshell-history-ring))
+      nil)
+     ((not (file-writable-p file))
+      (message "Cannot write history file %s" file))
+     (t
+      (let* ((ring eshell-history-ring)
+	     (index (ring-length ring)))
+	;; Write it all out into a buffer first.  Much faster, but
+	;; messier, than writing it one line at a time.
+	(with-temp-buffer
+	  (while (> index 0)
+	    (setq index (1- index))
+	    (insert (ring-ref ring index) ?\n))
+	  (eshell-with-private-file-modes
+	   (write-region (point-min) (point-max) file append
+			 'no-message))))))))
+
+(defun eshell-list-history ()
+  "List in help buffer the buffer's input history."
+  (interactive)
+  (let (prefix prelen)
+    (save-excursion
+      (if (re-search-backward "!\\(.+\\)" (line-beginning-position) t)
+	  (setq prefix (match-string 1)
+		prelen (length prefix))))
+    (if (or (not (ring-p eshell-history-ring))
+	    (ring-empty-p eshell-history-ring))
+	(message "No history")
+      (let ((history nil)
+	    (history-buffer " *Input History*")
+	    (index (1- (ring-length eshell-history-ring)))
+	    (conf (current-window-configuration)))
+	;; We have to build up a list ourselves from the ring vector.
+	(while (>= index 0)
+	  (let ((hist (eshell-get-history index)))
+	    (if (or (not prefix)
+		    (and (>= (length hist) prelen)
+			 (string= (substring hist 0 prelen) prefix)))
+		(setq history (cons hist history))))
+	  (setq index (1- index)))
+	;; Change "completion" to "history reference"
+	;; to make the display accurate.
+	(with-output-to-temp-buffer history-buffer
+	  (display-completion-list history)
+	  (set-buffer history-buffer)
+	  (forward-line 3)
+	  (while (search-backward "completion" nil 'move)
+	    (replace-match "history reference")))
+	(eshell-redisplay)
+	(message "Hit space to flush")
+	(let ((ch (read-event)))
+	  (if (eq ch ?\ )
+	      (set-window-configuration conf)
+	    (setq unread-command-events (list ch))))))))
+
+(defun eshell-hist-word-reference (ref)
+  "Return the word designator index referred to by REF."
+  (cond
+   ((string-match "^[0-9]+$" ref)
+    (string-to-number ref))
+   ((string= "^" ref) 1)
+   ((string= "$" ref) nil)
+   ((string= "%" ref)
+    (error "`%' history word designator not yet implemented"))))
+
+(defun eshell-hist-parse-arguments (&optional silent b e)
+  "Parse current command arguments in a history-code-friendly way."
+  (let ((end (or e (point)))
+	(begin (or b (save-excursion (eshell-bol) (point))))
+	(posb (list t))
+	(pose (list t))
+	(textargs (list t))
+	hist args)
+    (unless (catch 'eshell-incomplete
+	      (ignore
+	       (setq args (eshell-parse-arguments begin end))))
+      (save-excursion
+	(goto-char begin)
+	(while (< (point) end)
+	  (if (get-text-property (point) 'arg-begin)
+	      (nconc posb (list (point))))
+	  (if (get-text-property (point) 'arg-end)
+	      (nconc pose
+		     (list (if (= (1+ (point)) end)
+			       (1+ (point))
+			     (point)))))
+	  (forward-char))
+	(setq posb (cdr posb)
+	      pose (cdr pose))
+	(assert (= (length posb) (length args)))
+	(assert (<= (length posb) (length pose))))
+      (setq hist (buffer-substring-no-properties begin end))
+      (let ((b posb) (e pose))
+	(while b
+	  (nconc textargs
+		 (list (substring hist (- (car b) begin)
+				  (- (car e) begin))))
+	  (setq b (cdr b)
+		e (cdr e))))
+      (setq textargs (cdr textargs))
+      (assert (= (length textargs) (length args)))
+      (list textargs posb pose))))
+
+(defun eshell-expand-history-references (beg end)
+  "Parse and expand any history references in current input."
+  (let ((result (eshell-hist-parse-arguments t beg end)))
+    (when result
+      (let ((textargs (nreverse (nth 0 result)))
+	    (posb (nreverse (nth 1 result)))
+	    (pose (nreverse (nth 2 result))))
+	(save-excursion
+	  (while textargs
+	    (let ((str (eshell-history-reference (car textargs))))
+	      (unless (eq str (car textargs))
+		(goto-char (car posb))
+		(insert-and-inherit str)
+		(delete-char (- (car pose) (car posb)))))
+	    (setq textargs (cdr textargs)
+		  posb (cdr posb)
+		  pose (cdr pose))))))))
+
+(defun eshell-complete-history-reference ()
+  "Complete a history reference, by completing the event designator."
+  (let ((arg (pcomplete-actual-arg)))
+    (when (string-match "\\`![^:^$*%]*\\'" arg)
+      (setq pcomplete-stub (substring arg 1)
+	    pcomplete-last-completion-raw t)
+      (throw 'pcomplete-completions
+	     (let ((history nil)
+		   (index (1- (ring-length eshell-history-ring)))
+		   (stublen (length pcomplete-stub)))
+	       ;; We have to build up a list ourselves from the ring
+	       ;; vector.
+	       (while (>= index 0)
+		 (let ((hist (eshell-get-history index)))
+		   (if (and (>= (length hist) stublen)
+			    (string= (substring hist 0 stublen)
+				     pcomplete-stub)
+			    (string-match "^\\([^:^$*% \t\n]+\\)" hist))
+		       (setq history (cons (match-string 1 hist)
+					   history))))
+		 (setq index (1- index)))
+	       (let ((fhist (list t)))
+		 ;; uniqify the list, but preserve the order
+		 (while history
+		   (unless (member (car history) fhist)
+		     (nconc fhist (list (car history))))
+		   (setq history (cdr history)))
+		 (cdr fhist)))))))
+
+(defun eshell-history-reference (reference)
+  "Expand directory stack REFERENCE.
+The syntax used here was taken from the Bash info manual.
+Returns the resultant reference, or the same string REFERENCE if none
+matched."
+  ;; `^string1^string2^'
+  ;;      Quick Substitution.  Repeat the last command, replacing
+  ;;      STRING1 with STRING2.  Equivalent to `!!:s/string1/string2/'
+  (if (and (eshell-using-module 'eshell-pred)
+	   (string-match "\\^\\([^^]+\\)\\^\\([^^]+\\)\\^?\\s-*$"
+			 reference))
+      (setq reference (format "!!:s/%s/%s/"
+			      (match-string 1 reference)
+			      (match-string 2 reference))))
+  ;; `!'
+  ;;      Start a history substitution, except when followed by a
+  ;;      space, tab, the end of the line, = or (.
+  (if (not (string-match "^![^ \t\n=\(]" reference))
+      reference
+    (setq eshell-history-index nil)
+    (let ((event (eshell-hist-parse-event-designator reference)))
+      (unless event
+	(error "Could not find history event `%s'" reference))
+      (setq eshell-history-index (car event)
+	    reference (substring reference (cdr event))
+	    event (eshell-get-history eshell-history-index))
+      (if (not (string-match "^[:^$*%]" reference))
+	  event
+	(let ((word (eshell-hist-parse-word-designator
+		     event reference)))
+	  (unless word
+	    (error "Unable to honor word designator `%s'" reference))
+	  (unless (string-match "^[:^$*%][[$^*%0-9-]" reference)
+	    (setcdr word 0))
+	  (setq event (car word)
+		reference (substring reference (cdr word)))
+	  (if (not (and (eshell-using-module 'eshell-pred)
+			(string-match "^:" reference)))
+	      event
+	    (eshell-hist-parse-modifier event reference)))))))
+
+(defun eshell-hist-parse-event-designator (reference)
+  "Parse a history event designator beginning in REFERENCE."
+  (let* ((index (string-match eshell-hist-event-designator reference))
+	 (end (and index (match-end 0))))
+    (unless index
+      (error "Invalid history event designator `%s'" reference))
+    (let* ((event (match-string 1 reference))
+	   (pos
+	    (cond
+	     ((string= event "!") (ring-length eshell-history-ring))
+	     ((string= event "#") (error "!# not yet implemented"))
+	     ((string-match "^-?[0-9]+$" event)
+	      (let ((num (string-to-number event)))
+		(if (>= num 0)
+		    (- (ring-length eshell-history-ring) num)
+		  (1- (abs num)))))
+	     ((string-match "^\\(\\??\\)\\([^?]+\\)\\??$" event)
+	      (let ((pref (if (> (length (match-string 1 event)) 0)
+			      "" "^"))
+		    (str (match-string 2 event)))
+		(save-match-data
+		  (eshell-previous-matching-input-string-position
+		   (concat pref (regexp-quote str)) 1))))
+	     (t
+	      (error "Failed to parse event designator `%s'" event)))))
+      (and pos (cons pos end)))))
+
+(defun eshell-hist-parse-word-designator (hist reference)
+  "Parse a history word designator beginning for HIST in REFERENCE."
+  (let* ((index (string-match eshell-hist-word-designator reference))
+	 (end (and index (match-end 0))))
+    (unless (memq (aref reference 0) '(?: ?^ ?$ ?* ?%))
+      (error "Invalid history word designator `%s'" reference))
+    (let ((nth (match-string 1 reference))
+	  (mth (match-string 2 reference))
+	  (here (point))
+	  textargs)
+      (insert hist)
+      (setq textargs (car (eshell-hist-parse-arguments nil here (point))))
+      (delete-region here (point))
+      (if (string= nth "*")
+	  (if mth
+	      (error "Invalid history word designator `%s'"
+		     reference)
+	    (setq nth 1 mth "-$")))
+      (if (not mth)
+	  (if nth
+	      (setq mth nth)
+	    (setq nth 0 mth "$"))
+	(if (string= mth "-")
+	    (setq mth (- (length textargs) 2))
+	  (if (string= mth "*")
+	      (setq mth "$")
+	    (if (not (and (> (length mth) 1)
+			  (eq (aref mth 0) ?-)))
+		(error "Invalid history word designator `%s'"
+		       reference)
+	      (setq mth (substring mth 1))))))
+      (unless (numberp nth)
+	(setq nth (eshell-hist-word-reference nth)))
+      (unless (numberp mth)
+	(setq mth (eshell-hist-word-reference mth)))
+      (cons (mapconcat 'identity (eshell-sublist textargs nth mth) "")
+	    end))))
+
+(defun eshell-hist-parse-modifier (hist reference)
+  "Parse a history modifier beginning for HIST in REFERENCE."
+  (let ((here (point)))
+    (insert reference)
+    (prog1
+	(save-restriction
+	  (narrow-to-region here (point))
+	  (goto-char (point-min))
+	  (let ((modifiers (cdr (eshell-parse-modifiers))))
+	    (eshell-for mod modifiers
+	      (setq hist (funcall mod hist)))
+	    hist))
+      (delete-region here (point)))))
+
+(defun eshell-get-next-from-history ()
+  "After fetching a line from input history, this fetches the next.
+In other words, this recalls the input line after the line you
+recalled last.  You can use this to repeat a sequence of input lines."
+  (interactive)
+  (if eshell-save-history-index
+      (progn
+	(setq eshell-history-index (1+ eshell-save-history-index))
+	(eshell-next-input 1))
+    (message "No previous history command")))
+
+(defun eshell-search-arg (arg)
+  ;; First make sure there is a ring and that we are after the process
+  ;; mark
+  (if (and eshell-hist-move-to-end
+	   (< (point) eshell-last-output-end))
+      (goto-char eshell-last-output-end))
+  (cond ((or (null eshell-history-ring)
+	     (ring-empty-p eshell-history-ring))
+	 (error "Empty input ring"))
+	((zerop arg)
+	 ;; arg of zero resets search from beginning, and uses arg of
+	 ;; 1
+	 (setq eshell-history-index nil)
+	 1)
+	(t
+	 arg)))
+
+(defun eshell-search-start (arg)
+  "Index to start a directional search, starting at `eshell-history-index'."
+  (if eshell-history-index
+      ;; If a search is running, offset by 1 in direction of arg
+      (mod (+ eshell-history-index (if (> arg 0) 1 -1))
+	   (ring-length eshell-history-ring))
+    ;; For a new search, start from beginning or end, as appropriate
+    (if (>= arg 0)
+	0                               ; First elt for forward search
+      ;; Last elt for backward search
+      (1- (ring-length eshell-history-ring)))))
+
+(defun eshell-previous-input-string (arg)
+  "Return the string ARG places along the input ring.
+Moves relative to `eshell-history-index'."
+  (eshell-get-history (if eshell-history-index
+			  (mod (+ arg eshell-history-index)
+			       (ring-length eshell-history-ring))
+			arg)))
+
+(defun eshell-previous-input (arg)
+  "Cycle backwards through input history."
+  (interactive "*p")
+  (eshell-previous-matching-input "." arg))
+
+(defun eshell-next-input (arg)
+  "Cycle forwards through input history."
+  (interactive "*p")
+  (eshell-previous-input (- arg)))
+
+(defun eshell-previous-matching-input-string (regexp arg)
+  "Return the string matching REGEXP ARG places along the input ring.
+Moves relative to `eshell-history-index'."
+  (let* ((pos (eshell-previous-matching-input-string-position regexp arg)))
+    (if pos (eshell-get-history pos))))
+
+(defun eshell-previous-matching-input-string-position
+  (regexp arg &optional start)
+  "Return the index matching REGEXP ARG places along the input ring.
+Moves relative to START, or `eshell-history-index'."
+  (if (or (not (ring-p eshell-history-ring))
+	  (ring-empty-p eshell-history-ring))
+      (error "No history"))
+  (let* ((len (ring-length eshell-history-ring))
+	 (motion (if (> arg 0) 1 -1))
+	 (n (mod (- (or start (eshell-search-start arg)) motion) len))
+	 (tried-each-ring-item nil)
+	 (case-fold-search (eshell-under-windows-p))
+	 (prev nil))
+    ;; Do the whole search as many times as the argument says.
+    (while (and (/= arg 0) (not tried-each-ring-item))
+      ;; Step once.
+      (setq prev n
+	    n (mod (+ n motion) len))
+      ;; If we haven't reached a match, step some more.
+      (while (and (< n len) (not tried-each-ring-item)
+		  (not (string-match regexp (eshell-get-history n))))
+	(setq n (mod (+ n motion) len)
+	      ;; If we have gone all the way around in this search.
+	      tried-each-ring-item (= n prev)))
+      (setq arg (if (> arg 0) (1- arg) (1+ arg))))
+    ;; Now that we know which ring element to use, if we found it,
+    ;; return that.
+    (if (string-match regexp (eshell-get-history n))
+	n)))
+
+(defun eshell-previous-matching-input (regexp arg)
+  "Search backwards through input history for match for REGEXP.
+\(Previous history elements are earlier commands.)
+With prefix argument N, search for Nth previous match.
+If N is negative, find the next or Nth next match."
+  (interactive (eshell-regexp-arg "Previous input matching (regexp): "))
+  (setq arg (eshell-search-arg arg))
+  (let ((pos (eshell-previous-matching-input-string-position regexp arg)))
+    ;; Has a match been found?
+    (if (null pos)
+	(error "Not found")
+      (setq eshell-history-index pos)
+      (message "History item: %d" (- (ring-length eshell-history-ring) pos))
+       ;; Can't use kill-region as it sets this-command
+      (delete-region (save-excursion (eshell-bol) (point)) (point))
+      (insert-and-inherit (eshell-get-history pos)))))
+
+(defun eshell-next-matching-input (regexp arg)
+  "Search forwards through input history for match for REGEXP.
+\(Later history elements are more recent commands.)
+With prefix argument N, search for Nth following match.
+If N is negative, find the previous or Nth previous match."
+  (interactive (eshell-regexp-arg "Next input matching (regexp): "))
+  (eshell-previous-matching-input regexp (- arg)))
+
+(defun eshell-previous-matching-input-from-input (arg)
+  "Search backwards through input history for match for current input.
+\(Previous history elements are earlier commands.)
+With prefix argument N, search for Nth previous match.
+If N is negative, search forwards for the -Nth following match."
+  (interactive "p")
+  (if (not (memq last-command '(eshell-previous-matching-input-from-input
+				eshell-next-matching-input-from-input)))
+      ;; Starting a new search
+      (setq eshell-matching-input-from-input-string
+	    (buffer-substring (save-excursion (eshell-bol) (point))
+			      (point))
+	    eshell-history-index nil))
+  (eshell-previous-matching-input
+   (concat "^" (regexp-quote eshell-matching-input-from-input-string))
+   arg))
+
+(defun eshell-next-matching-input-from-input (arg)
+  "Search forwards through input history for match for current input.
+\(Following history elements are more recent commands.)
+With prefix argument N, search for Nth following match.
+If N is negative, search backwards for the -Nth previous match."
+  (interactive "p")
+  (eshell-previous-matching-input-from-input (- arg)))
+
+(defun eshell-test-imatch ()
+  "If isearch match good, put point at the beginning and return non-nil."
+  (if (get-text-property (point) 'history)
+      (progn (beginning-of-line) t)
+    (let ((before (point)))
+      (eshell-bol)
+      (if (and (not (bolp))
+	       (<= (point) before))
+	  t
+	(if isearch-forward
+	    (progn
+	      (end-of-line)
+	      (forward-char))
+	  (beginning-of-line)
+	  (backward-char))))))
+
+(defun eshell-return-to-prompt ()
+  "Once a search string matches, insert it at the end and go there."
+  (setq isearch-other-end nil)
+  (let ((found (eshell-test-imatch)) before)
+    (while (and (not found)
+		(setq before
+		      (funcall (if isearch-forward
+				   're-search-forward
+				 're-search-backward)
+			       isearch-string nil t)))
+      (setq found (eshell-test-imatch)))
+    (if (not found)
+	(progn
+	  (goto-char eshell-last-output-end)
+	  (delete-region (point) (point-max)))
+      (setq before (point))
+      (let ((text (buffer-substring-no-properties
+		   (point) (line-end-position)))
+	    (orig (marker-position eshell-last-output-end)))
+	(goto-char eshell-last-output-end)
+	(delete-region (point) (point-max))
+	(when (and text (> (length text) 0))
+	  (subst-char-in-string ?\177 ?\n text t)
+	  (insert text)
+	  (put-text-property (1- (point)) (point)
+			     'last-search-pos before)
+	  (set-marker eshell-last-output-end orig)
+	  (goto-char eshell-last-output-end))))))
+
+(defun eshell-prepare-for-search ()
+  "Make sure the old history file is at the beginning of the buffer."
+  (unless (get-text-property (point-min) 'history)
+    (save-excursion
+      (goto-char (point-min))
+      (let ((end (copy-marker (point) t)))
+	(insert-file-contents eshell-history-file-name)
+	(set-text-properties (point-min) end
+			     '(history t invisible t))))))
+
+(defun eshell-isearch-backward (&optional invert)
+  "Do incremental regexp search backward through past commands."
+  (interactive)
+  (let ((inhibit-read-only t) end)
+    (eshell-prepare-for-search)
+    (goto-char (point-max))
+    (set-marker eshell-last-output-end (point))
+    (delete-region (point) (point-max)))
+  (isearch-mode invert t 'eshell-return-to-prompt))
+
+(defun eshell-isearch-repeat-backward (&optional invert)
+  "Do incremental regexp search backward through past commands."
+  (interactive)
+  (let ((old-pos (get-text-property (1- (point-max))
+				    'last-search-pos)))
+    (when old-pos
+      (goto-char old-pos)
+      (if invert
+	  (end-of-line)
+	(backward-char)))
+    (setq isearch-forward invert)
+    (isearch-search-and-update)))
+
+(defun eshell-isearch-forward ()
+  "Do incremental regexp search backward through past commands."
+  (interactive)
+  (eshell-isearch-backward t))
+
+(defun eshell-isearch-repeat-forward ()
+  "Do incremental regexp search backward through past commands."
+  (interactive)
+  (eshell-isearch-repeat-backward t))
+
+(defun eshell-isearch-cancel ()
+  (interactive)
+  (goto-char eshell-last-output-end)
+  (delete-region (point) (point-max))
+  (call-interactively 'isearch-cancel))
+
+(defun eshell-isearch-abort ()
+  (interactive)
+  (goto-char eshell-last-output-end)
+  (delete-region (point) (point-max))
+  (call-interactively 'isearch-abort))
+
+(defun eshell-isearch-delete-char ()
+  (interactive)
+  (save-excursion
+  (isearch-delete-char)))
+
+(defun eshell-isearch-return ()
+  (interactive)
+  (isearch-done)
+  (eshell-send-input))
+
+;;; em-hist.el ends here