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