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