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