38414
|
1 ;;; em-hist.el --- history list management
|
29876
|
2
|
74509
|
3 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
|
79707
|
4 ;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
29876
|
5
|
32526
|
6 ;; Author: John Wiegley <johnw@gnu.org>
|
|
7
|
29876
|
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
|
78220
|
12 ;; the Free Software Foundation; either version 3, or (at your option)
|
29876
|
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
|
64085
|
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
23 ;; Boston, MA 02110-1301, USA.
|
29876
|
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)
|
87068
|
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)
|
29876
|
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
|
49266
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
105 (defcustom eshell-save-history-on-exit 'ask
|
29876
|
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.
|
49266
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
110 If set to nil, it means never save history on termination of Emacs.
|
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
111 If set to `ask', ask if any Eshell buffers are open at exit time.
|
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
112 If set to t, history will always be saved, silently."
|
29876
|
113 :type '(choice (const :tag "Never" nil)
|
49266
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
114 (const :tag "Ask" ask)
|
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
115 (const :tag "Always save" t))
|
29876
|
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
|
64384
|
204 (defvar eshell-rebind-keys-alist)
|
|
205
|
29876
|
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
|
43322
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
217 (if (and (eshell-using-module 'eshell-rebind)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
218 (not eshell-non-interactive-p))
|
64384
|
219 (let ((rebind-alist eshell-rebind-keys-alist))
|
29876
|
220 (make-local-variable 'eshell-rebind-keys-alist)
|
64384
|
221 (setq eshell-rebind-keys-alist
|
|
222 (append rebind-alist eshell-hist-rebind-keys-alist))
|
29876
|
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)
|
43322
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
270
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
271 (if (minibuffer-window-active-p (selected-window))
|
49266
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
272 (set (make-local-variable 'eshell-save-history-on-exit) nil)
|
43322
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
273 (set (make-local-variable 'eshell-history-ring) nil)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
274 (if eshell-history-file-name
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
275 (eshell-read-history nil t))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
276
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
277 (add-hook 'eshell-exit-hook 'eshell-write-history nil t))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
278
|
29876
|
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
|
49266
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
299 eshell-save-history-on-exit
|
f4d32be2d504
(eshell-save-history-on-exit): Renamed `eshell-ask-to-save-history'
John Wiegley <johnw@newartisans.com>
diff
changeset
|
300 (or (eq eshell-save-history-on-exit t)
|
29876
|
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."
|
31241
|
363 (ring-ref (or ring eshell-history-ring) index))
|
29876
|
364
|
43322
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
365 (defun eshell-add-input-to-history (input)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
366 "Add the string INPUT to the history ring.
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
367 Input is entered into the input history ring, if the value of
|
29876
|
368 variable `eshell-input-filter' returns non-nil when called on the
|
|
369 input."
|
43322
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
370 (if (and (funcall eshell-input-filter input)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
371 (or (null eshell-hist-ignoredups)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
372 (not (ring-p eshell-history-ring))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
373 (ring-empty-p eshell-history-ring)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
374 (not (string-equal (eshell-get-history 0) input))))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
375 (eshell-put-history input))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
376 (setq eshell-save-history-index eshell-history-index)
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
377 (setq eshell-history-index nil))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
378
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
379 (defun eshell-add-command-to-history ()
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
380 "Add the command entered at `eshell-command's prompt to the history ring.
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
381 The command is added to the input history ring, if the value of
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
382 variable `eshell-input-filter' returns non-nil when called on the
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
383 command.
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
384
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
385 This function is supposed to be called from the minibuffer, presumably
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
386 as a minibuffer-exit-hook."
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
387 (eshell-add-input-to-history
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
388 (buffer-substring (minibuffer-prompt-end) (point-max))))
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
389
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
390 (defun eshell-add-to-history ()
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
391 "Add last Eshell command to the history ring.
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
392 The command is entered into the input history ring, if the value of
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
393 variable `eshell-input-filter' returns non-nil when called on the
|
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
394 command."
|
29876
|
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))))
|
43322
80c00f33bf18
(eshell-hist-initialize): When in the minibuffer, use the global value
John Wiegley <johnw@newartisans.com>
diff
changeset
|
398 (eshell-add-input-to-history input))))
|
29876
|
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)))
|
31241
|
440 (ring-insert-at-beginning
|
|
441 ring (subst-char-in-string ?\177 ?\n history))))
|
29876
|
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))
|
31241
|
473 (let ((start (point)))
|
|
474 (insert (ring-ref ring index) ?\n)
|
|
475 (subst-char-in-region start (1- (point)) ?\n ?\177)))
|
29876
|
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
|
66114
|
506 (display-completion-list history prefix)
|
29876
|
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)
|
53871
|
526 (error "`%%' history word designator not yet implemented"))))
|
29876
|
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)
|
46852
|
808 (case-fold-search (eshell-under-windows-p))
|
29876
|
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)
|
37323
11b33211a908
(eshell-previous-matching-input): Don't display "History item" if the
John Wiegley <johnw@newartisans.com>
diff
changeset
|
839 (unless (minibuffer-window-active-p (selected-window))
|
11b33211a908
(eshell-previous-matching-input): Don't display "History item" if the
John Wiegley <johnw@newartisans.com>
diff
changeset
|
840 (message "History item: %d" (- (ring-length eshell-history-ring) pos)))
|
29876
|
841 ;; Can't use kill-region as it sets this-command
|
59196
|
842 (delete-region eshell-last-output-end (point))
|
29876
|
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
|
87068
|
987 (provide 'em-hist)
|
|
988
|
52401
|
989 ;;; arch-tag: 1a847333-f864-4b96-9acd-b549d620b6c6
|
29876
|
990 ;;; em-hist.el ends here
|