| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
(provide 'em-hist) |
|---|
| 26 |
|
|---|
| 27 |
(eval-when-compile (require 'esh-maint)) |
|---|
| 28 |
(require 'eshell) |
|---|
| 29 |
|
|---|
| 30 |
(defgroup eshell-hist nil |
|---|
| 31 |
"This module provides command history management." |
|---|
| 32 |
:tag "History list management" |
|---|
| 33 |
:group 'eshell-module) |
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
(require 'ring) |
|---|
| 71 |
(require 'esh-opt) |
|---|
| 72 |
(require 'em-pred) |
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
(defcustom eshell-hist-load-hook '(eshell-hist-initialize) |
|---|
| 77 |
"*A list of functions to call when loading `eshell-hist'." |
|---|
| 78 |
:type 'hook |
|---|
| 79 |
:group 'eshell-hist) |
|---|
| 80 |
|
|---|
| 81 |
(defcustom eshell-hist-unload-hook |
|---|
| 82 |
(list |
|---|
| 83 |
(function |
|---|
| 84 |
(lambda () |
|---|
| 85 |
(remove-hook 'kill-emacs-hook 'eshell-save-some-history)))) |
|---|
| 86 |
"*A hook that gets run when `eshell-hist' is unloaded." |
|---|
| 87 |
:type 'hook |
|---|
| 88 |
:group 'eshell-hist) |
|---|
| 89 |
|
|---|
| 90 |
(defcustom eshell-history-file-name |
|---|
| 91 |
(concat eshell-directory-name "history") |
|---|
| 92 |
"*If non-nil, name of the file to read/write input history. |
|---|
| 93 |
See also `eshell-read-history' and `eshell-write-history'. |
|---|
| 94 |
If it is nil, Eshell will use the value of HISTFILE." |
|---|
| 95 |
:type 'file |
|---|
| 96 |
:group 'eshell-hist) |
|---|
| 97 |
|
|---|
| 98 |
(defcustom eshell-history-size 128 |
|---|
| 99 |
"*Size of the input history ring. If nil, use envvar HISTSIZE." |
|---|
| 100 |
:type '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 'ask |
|---|
| 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 |
|
|---|
| 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 |
|
|---|
| 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 |
(setq eshell-history-size (getenv "HISTSIZE"))) |
|---|
| 267 |
|
|---|
| 268 |
(make-local-variable 'eshell-history-file-name) |
|---|
| 269 |
(or eshell-history-file-name |
|---|
| 270 |
(setq eshell-history-file-name (getenv "HISTFILE"))) |
|---|
| 271 |
|
|---|
| 272 |
(make-local-variable 'eshell-history-index) |
|---|
| 273 |
(make-local-variable 'eshell-save-history-index) |
|---|
| 274 |
|
|---|
| 275 |
(if (minibuffer-window-active-p (selected-window)) |
|---|
| 276 |
(set (make-local-variable 'eshell-save-history-on-exit) nil) |
|---|
| 277 |
(set (make-local-variable 'eshell-history-ring) nil) |
|---|
| 278 |
(if eshell-history-file-name |
|---|
| 279 |
(eshell-read-history nil t)) |
|---|
| 280 |
|
|---|
| 281 |
(add-hook 'eshell-exit-hook 'eshell-write-history nil t)) |
|---|
| 282 |
|
|---|
| 283 |
(unless eshell-history-ring |
|---|
| 284 |
(setq eshell-history-ring (make-ring eshell-history-size))) |
|---|
| 285 |
|
|---|
| 286 |
(add-hook 'eshell-exit-hook 'eshell-write-history nil t) |
|---|
| 287 |
|
|---|
| 288 |
(add-hook 'kill-emacs-hook 'eshell-save-some-history) |
|---|
| 289 |
|
|---|
| 290 |
(make-local-variable 'eshell-input-filter-functions) |
|---|
| 291 |
(add-hook 'eshell-input-filter-functions 'eshell-add-to-history nil t) |
|---|
| 292 |
|
|---|
| 293 |
(define-key eshell-command-map [(control ?l)] 'eshell-list-history) |
|---|
| 294 |
(define-key eshell-command-map [(control ?x)] 'eshell-get-next-from-history)) |
|---|
| 295 |
|
|---|
| 296 |
(defun eshell-save-some-history () |
|---|
| 297 |
"Save the history for any open Eshell buffers." |
|---|
| 298 |
(eshell-for buf (buffer-list) |
|---|
| 299 |
(if (buffer-live-p buf) |
|---|
| 300 |
(with-current-buffer buf |
|---|
| 301 |
(if (and eshell-mode |
|---|
| 302 |
eshell-history-file-name |
|---|
| 303 |
eshell-save-history-on-exit |
|---|
| 304 |
(or (eq eshell-save-history-on-exit t) |
|---|
| 305 |
(y-or-n-p |
|---|
| 306 |
(format "Save input history for Eshell buffer `%s'? " |
|---|
| 307 |
(buffer-name buf))))) |
|---|
| 308 |
(eshell-write-history)))))) |
|---|
| 309 |
|
|---|
| 310 |
(defun eshell/history (&rest args) |
|---|
| 311 |
"List in help buffer the buffer's input history." |
|---|
| 312 |
(eshell-init-print-buffer) |
|---|
| 313 |
(eshell-eval-using-options |
|---|
| 314 |
"history" args |
|---|
| 315 |
'((?r "read" nil read-history |
|---|
| 316 |
"read from history file to current history list") |
|---|
| 317 |
(?w "write" nil write-history |
|---|
| 318 |
"write current history list to history file") |
|---|
| 319 |
(?a "append" nil append-history |
|---|
| 320 |
"append current history list to history file") |
|---|
| 321 |
(?h "help" nil nil "display this usage message") |
|---|
| 322 |
:usage "[n] [-rwa [filename]]" |
|---|
| 323 |
:post-usage |
|---|
| 324 |
"When Eshell is started, history is read from `eshell-history-file-name'. |
|---|
| 325 |
This is also the location where history info will be saved by this command, |
|---|
| 326 |
unless a different file is specified on the command line.") |
|---|
| 327 |
(and (or (not (ring-p eshell-history-ring)) |
|---|
| 328 |
(ring-empty-p eshell-history-ring)) |
|---|
| 329 |
(error "No history")) |
|---|
| 330 |
(let (length command file) |
|---|
| 331 |
(when (and args (string-match "^[0-9]+$" (car args))) |
|---|
| 332 |
(setq length (min (eshell-convert (car args)) |
|---|
| 333 |
(ring-length eshell-history-ring)) |
|---|
| 334 |
args (cdr args))) |
|---|
| 335 |
(and length |
|---|
| 336 |
(or read-history write-history append-history) |
|---|
| 337 |
(error "history: extra arguments")) |
|---|
| 338 |
(when (and args (stringp (car args))) |
|---|
| 339 |
(setq file (car args) |
|---|
| 340 |
args (cdr args))) |
|---|
| 341 |
(cond |
|---|
| 342 |
(read-history (eshell-read-history file)) |
|---|
| 343 |
(write-history (eshell-write-history file)) |
|---|
| 344 |
(append-history (eshell-write-history file t)) |
|---|
| 345 |
(t |
|---|
| 346 |
(let* ((history nil) |
|---|
| 347 |
(index (1- (or length (ring-length eshell-history-ring)))) |
|---|
| 348 |
(ref (- (ring-length eshell-history-ring) index))) |
|---|
| 349 |
|
|---|
| 350 |
(while (>= index 0) |
|---|
| 351 |
(eshell-buffered-print |
|---|
| 352 |
(format "%5d %s\n" ref (eshell-get-history index))) |
|---|
| 353 |
(setq index (1- index) |
|---|
| 354 |
ref (1+ ref))))))) |
|---|
| 355 |
(eshell-flush) |
|---|
| 356 |
nil)) |
|---|
| 357 |
|
|---|
| 358 |
(defun eshell-put-history (input &optional ring at-beginning) |
|---|
| 359 |
"Put a new input line into the history ring." |
|---|
| 360 |
(unless ring (setq ring eshell-history-ring)) |
|---|
| 361 |
(if at-beginning |
|---|
| 362 |
(ring-insert-at-beginning ring input) |
|---|
| 363 |
(ring-insert ring input))) |
|---|
| 364 |
|
|---|
| 365 |
(defun eshell-get-history (index &optional ring) |
|---|
| 366 |
"Get an input line from the history ring." |
|---|
| 367 |
(ring-ref (or ring eshell-history-ring) index)) |
|---|
| 368 |
|
|---|
| 369 |
(defun eshell-add-input-to-history (input) |
|---|
| 370 |
"Add the string INPUT to the history ring. |
|---|
| 371 |
Input is entered into the input history ring, if the value of |
|---|
| 372 |
variable `eshell-input-filter' returns non-nil when called on the |
|---|
| 373 |
input." |
|---|
| 374 |
(if (and (funcall eshell-input-filter input) |
|---|
| 375 |
(or (null eshell-hist-ignoredups) |
|---|
| 376 |
(not (ring-p eshell-history-ring)) |
|---|
| 377 |
(ring-empty-p eshell-history-ring) |
|---|
| 378 |
(not (string-equal (eshell-get-history 0) input)))) |
|---|
| 379 |
(eshell-put-history input)) |
|---|
| 380 |
(setq eshell-save-history-index eshell-history-index) |
|---|
| 381 |
(setq eshell-history-index nil)) |
|---|
| 382 |
|
|---|
| 383 |
(defun eshell-add-command-to-history () |
|---|
| 384 |
"Add the command entered at `eshell-command's prompt to the history ring. |
|---|
| 385 |
The command is added to the input history ring, if the value of |
|---|
| 386 |
variable `eshell-input-filter' returns non-nil when called on the |
|---|
| 387 |
command. |
|---|
| 388 |
|
|---|
| 389 |
This function is supposed to be called from the minibuffer, presumably |
|---|
| 390 |
as a minibuffer-exit-hook." |
|---|
| 391 |
(eshell-add-input-to-history |
|---|
| 392 |
(buffer-substring (minibuffer-prompt-end) (point-max)))) |
|---|
| 393 |
|
|---|
| 394 |
(defun eshell-add-to-history () |
|---|
| 395 |
"Add last Eshell command to the history ring. |
|---|
| 396 |
The command is entered into the input history ring, if the value of |
|---|
| 397 |
variable `eshell-input-filter' returns non-nil when called on the |
|---|
| 398 |
command." |
|---|
| 399 |
(when (> (1- eshell-last-input-end) eshell-last-input-start) |
|---|
| 400 |
(let ((input (buffer-substring eshell-last-input-start |
|---|
| 401 |
(1- eshell-last-input-end)))) |
|---|
| 402 |
(eshell-add-input-to-history input)))) |
|---|
| 403 |
|
|---|
| 404 |
(defun eshell-read-history (&optional filename silent) |
|---|
| 405 |
"Sets the buffer's `eshell-history-ring' from a history file. |
|---|
| 406 |
The name of the file is given by the variable |
|---|
| 407 |
`eshell-history-file-name'. The history ring is of size |
|---|
| 408 |
`eshell-history-size', regardless of file size. If |
|---|
| 409 |
`eshell-history-file-name' is nil this function does nothing. |
|---|
| 410 |
|
|---|
| 411 |
If the optional argument SILENT is non-nil, we say nothing about a |
|---|
| 412 |
failure to read the history file. |
|---|
| 413 |
|
|---|
| 414 |
This function is useful for major mode commands and mode hooks. |
|---|
| 415 |
|
|---|
| 416 |
The structure of the history file should be one input command per |
|---|
| 417 |
line, with the most recent command last. See also |
|---|
| 418 |
`eshell-hist-ignoredups' and `eshell-write-history'." |
|---|
| 419 |
(let ((file (or filename eshell-history-file-name))) |
|---|
| 420 |
(cond |
|---|
| 421 |
((or (null file) |
|---|
| 422 |
(equal file "")) |
|---|
| 423 |
nil) |
|---|
| 424 |
((not (file-readable-p file)) |
|---|
| 425 |
(or silent |
|---|
| 426 |
(message "Cannot read history file %s" file))) |
|---|
| 427 |
(t |
|---|
| 428 |
(let* ((count 0) |
|---|
| 429 |
(size eshell-history-size) |
|---|
| 430 |
(ring (make-ring size)) |
|---|
| 431 |
(ignore-dups eshell-hist-ignoredups)) |
|---|
| 432 |
(with-temp-buffer |
|---|
| 433 |
(insert-file-contents file) |
|---|
| 434 |
|
|---|
| 435 |
|
|---|
| 436 |
(goto-char (point-max)) |
|---|
| 437 |
(while (and (< count size) |
|---|
| 438 |
(re-search-backward "^[ \t]*\\([^#\n].*\\)[ \t]*$" |
|---|
| 439 |
nil t)) |
|---|
| 440 |
(let ((history (match-string 1))) |
|---|
| 441 |
(if (or (null ignore-dups) |
|---|
| 442 |
(ring-empty-p ring) |
|---|
| 443 |
(not (string-equal (ring-ref ring 0) history))) |
|---|
| 444 |
(ring-insert-at-beginning |
|---|
| 445 |
ring (subst-char-in-string ?\177 ?\n history)))) |
|---|
| 446 |
(setq count (1+ count)))) |
|---|
| 447 |
(setq eshell-history-ring ring |
|---|
| 448 |
eshell-history-index nil)))))) |
|---|
| 449 |
|
|---|
| 450 |
(defun eshell-write-history (&optional filename append) |
|---|
| 451 |
"Writes the buffer's `eshell-history-ring' to a history file. |
|---|
| 452 |
The name of the file is given by the variable |
|---|
| 453 |
`eshell-history-file-name'. The original contents of the file are |
|---|
| 454 |
lost if `eshell-history-ring' is not empty. If |
|---|
| 455 |
`eshell-history-file-name' is nil this function does nothing. |
|---|
| 456 |
|
|---|
| 457 |
Useful within process sentinels. |
|---|
| 458 |
|
|---|
| 459 |
See also `eshell-read-history'." |
|---|
| 460 |
(let ((file (or filename eshell-history-file-name))) |
|---|
| 461 |
(cond |
|---|
| 462 |
((or (null file) |
|---|
| 463 |
(equal file "") |
|---|
| 464 |
(null eshell-history-ring) |
|---|
| 465 |
(ring-empty-p eshell-history-ring)) |
|---|
| 466 |
nil) |
|---|
| 467 |
((not (file-writable-p file)) |
|---|
| 468 |
(message "Cannot write history file %s" file)) |
|---|
| 469 |
(t |
|---|
| 470 |
(let* ((ring eshell-history-ring) |
|---|
| 471 |
(index (ring-length ring))) |
|---|
| 472 |
|
|---|
| 473 |
|
|---|
| 474 |
(with-temp-buffer |
|---|
| 475 |
(while (> index 0) |
|---|
| 476 |
(setq index (1- index)) |
|---|
| 477 |
(let ((start (point))) |
|---|
| 478 |
(insert (ring-ref ring index) ?\n) |
|---|
| 479 |
(subst-char-in-region start (1- (point)) ?\n ?\177))) |
|---|
| 480 |
(eshell-with-private-file-modes |
|---|
| 481 |
(write-region (point-min) (point-max) file append |
|---|
| 482 |
'no-message)))))))) |
|---|
| 483 |
|
|---|
| 484 |
(defun eshell-list-history () |
|---|
| 485 |
"List in help buffer the buffer's input history." |
|---|
| 486 |
(interactive) |
|---|
| 487 |
(let (prefix prelen) |
|---|
| 488 |
(save-excursion |
|---|
| 489 |
(if (re-search-backward "!\\(.+\\)" (line-beginning-position) t) |
|---|
| 490 |
(setq prefix (match-string 1) |
|---|
| 491 |
prelen (length prefix)))) |
|---|
| 492 |
(if (or (not (ring-p eshell-history-ring)) |
|---|
| 493 |
(ring-empty-p eshell-history-ring)) |
|---|
| 494 |
(message "No history") |
|---|
| 495 |
(let ((history nil) |
|---|
| 496 |
(history-buffer " *Input History*") |
|---|
| 497 |
(index (1- (ring-length eshell-history-ring))) |
|---|
| 498 |
(conf (current-window-configuration))) |
|---|
| 499 |
|
|---|
| 500 |
(while (>= index 0) |
|---|
| 501 |
(let ((hist (eshell-get-history index))) |
|---|
| 502 |
(if (or (not prefix) |
|---|
| 503 |
(and (>= (length hist) prelen) |
|---|
| 504 |
(string= (substring hist 0 prelen) prefix))) |
|---|
| 505 |
(setq history (cons hist history)))) |
|---|
| 506 |
(setq index (1- index))) |
|---|
| 507 |
|
|---|
| 508 |
|
|---|
| 509 |
(with-output-to-temp-buffer history-buffer |
|---|
| 510 |
(display-completion-list history prefix) |
|---|
| 511 |
(set-buffer history-buffer) |
|---|
| 512 |
(forward-line 3) |
|---|
| 513 |
(while (search-backward "completion" nil 'move) |
|---|
| 514 |
(replace-match "history reference"))) |
|---|
| 515 |
(eshell-redisplay) |
|---|
| 516 |
(message "Hit space to flush") |
|---|
| 517 |
(let ((ch (read-event))) |
|---|
| 518 |
(if (eq ch ?\ ) |
|---|
| 519 |
(set-window-configuration conf) |
|---|
| 520 |
(setq unread-command-events (list ch)))))))) |
|---|
| 521 |
|
|---|
| 522 |
(defun eshell-hist-word-reference (ref) |
|---|
| 523 |
"Return the word designator index referred to by REF." |
|---|
| 524 |
(cond |
|---|
| 525 |
((string-match "^[0-9]+$" ref) |
|---|
| 526 |
(string-to-number ref)) |
|---|
| 527 |
((string= "^" ref) 1) |
|---|
| 528 |
((string= "$" ref) nil) |
|---|
| 529 |
((string= "%" ref) |
|---|
| 530 |
(error "`%%' history word designator not yet implemented")))) |
|---|
| 531 |
|
|---|
| 532 |
(defun eshell-hist-parse-arguments (&optional silent b e) |
|---|
| 533 |
"Parse current command arguments in a history-code-friendly way." |
|---|
| 534 |
(let ((end (or e (point))) |
|---|
| 535 |
(begin (or b (save-excursion (eshell-bol) (point)))) |
|---|
| 536 |
(posb (list t)) |
|---|
| 537 |
(pose (list t)) |
|---|
| 538 |
(textargs (list t)) |
|---|
| 539 |
hist args) |
|---|
| 540 |
(unless (catch 'eshell-incomplete |
|---|
| 541 |
(ignore |
|---|
| 542 |
(setq args (eshell-parse-arguments begin end)))) |
|---|
| 543 |
(save-excursion |
|---|
| 544 |
(goto-char begin) |
|---|
| 545 |
(while (< (point) end) |
|---|
| 546 |
(if (get-text-property (point) 'arg-begin) |
|---|
| 547 |
(nconc posb (list (point)))) |
|---|
| 548 |
(if (get-text-property (point) 'arg-end) |
|---|
| 549 |
(nconc pose |
|---|
| 550 |
(list (if (= (1+ (point)) end) |
|---|
| 551 |
(1+ (point)) |
|---|
| 552 |
(point))))) |
|---|
| 553 |
(forward-char)) |
|---|
| 554 |
(setq posb (cdr posb) |
|---|
| 555 |
pose (cdr pose)) |
|---|
| 556 |
(assert (= (length posb) (length args))) |
|---|
| 557 |
(assert (<= (length posb) (length pose)))) |
|---|
| 558 |
(setq hist (buffer-substring-no-properties begin end)) |
|---|
| 559 |
(let ((b posb) (e pose)) |
|---|
| 560 |
(while b |
|---|
| 561 |
(nconc textargs |
|---|
| 562 |
(list (substring hist (- (car b) begin) |
|---|
| 563 |
(- (car e) begin)))) |
|---|
| 564 |
(setq b (cdr b) |
|---|
| 565 |
e (cdr e)))) |
|---|
| 566 |
(setq textargs (cdr textargs)) |
|---|
| 567 |
(assert (= (length textargs) (length args))) |
|---|
| 568 |
(list textargs posb pose)))) |
|---|
| 569 |
|
|---|
| 570 |
(defun eshell-expand-history-references (beg end) |
|---|
| 571 |
"Parse and expand any history references in current input." |
|---|
| 572 |
(let ((result (eshell-hist-parse-arguments t beg end))) |
|---|
| 573 |
(when result |
|---|
| 574 |
(let ((textargs (nreverse (nth 0 result))) |
|---|
| 575 |
(posb (nreverse (nth 1 result))) |
|---|
| 576 |
(pose (nreverse (nth 2 result)))) |
|---|
| 577 |
(save-excursion |
|---|
| 578 |
(while textargs |
|---|
| 579 |
(let ((str (eshell-history-reference (car textargs)))) |
|---|
| 580 |
(unless (eq str (car textargs)) |
|---|
| 581 |
(goto-char (car posb)) |
|---|
| 582 |
(insert-and-inherit str) |
|---|
| 583 |
(delete-char (- (car pose) (car posb))))) |
|---|
| 584 |
(setq textargs (cdr textargs) |
|---|
| 585 |
posb (cdr posb) |
|---|
| 586 |
pose (cdr pose)))))))) |
|---|
| 587 |
|
|---|
| 588 |
(defun eshell-complete-history-reference () |
|---|
| 589 |
"Complete a history reference, by completing the event designator." |
|---|
| 590 |
(let ((arg (pcomplete-actual-arg))) |
|---|
| 591 |
(when (string-match "\\`![^:^$*%]*\\'" arg) |
|---|
| 592 |
(setq pcomplete-stub (substring arg 1) |
|---|
| 593 |
pcomplete-last-completion-raw t) |
|---|
| 594 |
(throw 'pcomplete-completions |
|---|
| 595 |
(let ((history nil) |
|---|
| 596 |
(index (1- (ring-length eshell-history-ring))) |
|---|
| 597 |
(stublen (length pcomplete-stub))) |
|---|
| 598 |
|
|---|
| 599 |
|
|---|
| 600 |
(while (>= index 0) |
|---|
| 601 |
(let ((hist (eshell-get-history index))) |
|---|
| 602 |
(if (and (>= (length hist) stublen) |
|---|
| 603 |
(string= (substring hist 0 stublen) |
|---|
| 604 |
pcomplete-stub) |
|---|
| 605 |
(string-match "^\\([^:^$*% \t\n]+\\)" hist)) |
|---|
| 606 |
(setq history (cons (match-string 1 hist) |
|---|
| 607 |
history)))) |
|---|
| 608 |
(setq index (1- index))) |
|---|
| 609 |
(let ((fhist (list t))) |
|---|
| 610 |
|
|---|
| 611 |
(while history |
|---|
| 612 |
(unless (member (car history) fhist) |
|---|
| 613 |
(nconc fhist (list (car history)))) |
|---|
| 614 |
(setq history (cdr history))) |
|---|
| 615 |
(cdr fhist))))))) |
|---|
| 616 |
|
|---|
| 617 |
(defun eshell-history-reference (reference) |
|---|
| 618 |
"Expand directory stack REFERENCE. |
|---|
| 619 |
The syntax used here was taken from the Bash info manual. |
|---|
| 620 |
Returns the resultant reference, or the same string REFERENCE if none |
|---|
| 621 |
matched." |
|---|
| 622 |
|
|---|
| 623 |
|
|---|
| 624 |
|
|---|
| 625 |
(if (and (eshell-using-module 'eshell-pred) |
|---|
| 626 |
(string-match "\\^\\([^^]+\\)\\^\\([^^]+\\)\\^?\\s-*$" |
|---|
| 627 |
reference)) |
|---|
| 628 |
(setq reference (format "!!:s/%s/%s/" |
|---|
| 629 |
(match-string 1 reference) |
|---|
| 630 |
(match-string 2 reference)))) |
|---|
| 631 |
|
|---|
| 632 |
|
|---|
| 633 |
|
|---|
| 634 |
(if (not (string-match "^![^ \t\n=\(]" reference)) |
|---|
| 635 |
reference |
|---|
| 636 |
(setq eshell-history-index nil) |
|---|
| 637 |
(let ((event (eshell-hist-parse-event-designator reference))) |
|---|
| 638 |
(unless event |
|---|
| 639 |
(error "Could not find history event `%s'" reference)) |
|---|
| 640 |
(setq eshell-history-index (car event) |
|---|
| 641 |
reference (substring reference (cdr event)) |
|---|
| 642 |
event (eshell-get-history eshell-history-index)) |
|---|
| 643 |
(if (not (string-match "^[:^$*%]" reference)) |
|---|
| 644 |
event |
|---|
| 645 |
(let ((word (eshell-hist-parse-word-designator |
|---|
| 646 |
event reference))) |
|---|
| 647 |
(unless word |
|---|
| 648 |
(error "Unable to honor word designator `%s'" reference)) |
|---|
| 649 |
(unless (string-match "^[:^$*%][[$^*%0-9-]" reference) |
|---|
| 650 |
(setcdr word 0)) |
|---|
| 651 |
(setq event (car word) |
|---|
| 652 |
reference (substring reference (cdr word))) |
|---|
| 653 |
(if (not (and (eshell-using-module 'eshell-pred) |
|---|
| 654 |
(string-match "^:" reference))) |
|---|
| 655 |
event |
|---|
| 656 |
(eshell-hist-parse-modifier event reference))))))) |
|---|
| 657 |
|
|---|
| 658 |
(defun eshell-hist-parse-event-designator (reference) |
|---|
| 659 |
"Parse a history event designator beginning in REFERENCE." |
|---|
| 660 |
(let* ((index (string-match eshell-hist-event-designator reference)) |
|---|
| 661 |
(end (and index (match-end 0)))) |
|---|
| 662 |
(unless index |
|---|
| 663 |
(error "Invalid history event designator `%s'" reference)) |
|---|
| 664 |
(let* ((event (match-string 1 reference)) |
|---|
| 665 |
(pos |
|---|
| 666 |
(cond |
|---|
| 667 |
((string= event "!") (ring-length eshell-history-ring)) |
|---|
| 668 |
((string= event "#") (error "!# not yet implemented")) |
|---|
| 669 |
((string-match "^-?[0-9]+$" event) |
|---|
| 670 |
(let ((num (string-to-number event))) |
|---|
| 671 |
(if (>= num 0) |
|---|
| 672 |
(- (ring-length eshell-history-ring) num) |
|---|
| 673 |
(1- (abs num))))) |
|---|
| 674 |
((string-match "^\\(\\??\\)\\([^?]+\\)\\??$" event) |
|---|
| 675 |
(let ((pref (if (> (length (match-string 1 event)) 0) |
|---|
| 676 |
"" "^")) |
|---|
| 677 |
(str (match-string 2 event))) |
|---|
| 678 |
(save-match-data |
|---|
| 679 |
(eshell-previous-matching-input-string-position |
|---|
| 680 |
(concat pref (regexp-quote str)) 1)))) |
|---|
| 681 |
(t |
|---|
| 682 |
(error "Failed to parse event designator `%s'" event))))) |
|---|
| 683 |
(and pos (cons pos end))))) |
|---|
| 684 |
|
|---|
| 685 |
(defun eshell-hist-parse-word-designator (hist reference) |
|---|
| 686 |
"Parse a history word designator beginning for HIST in REFERENCE." |
|---|
| 687 |
(let* ((index (string-match eshell-hist-word-designator reference)) |
|---|
| 688 |
(end (and index (match-end 0)))) |
|---|
| 689 |
(unless (memq (aref reference 0) '(?: ?^ ?$ ?* ?%)) |
|---|
| 690 |
(error "Invalid history word designator `%s'" reference)) |
|---|
| 691 |
(let ((nth (match-string 1 reference)) |
|---|
| 692 |
(mth (match-string 2 reference)) |
|---|
| 693 |
(here (point)) |
|---|
| 694 |
textargs) |
|---|
| 695 |
(insert hist) |
|---|
| 696 |
(setq textargs (car (eshell-hist-parse-arguments nil here (point)))) |
|---|
| 697 |
(delete-region here (point)) |
|---|
| 698 |
(if (string= nth "*") |
|---|
| 699 |
(if mth |
|---|
| 700 |
(error "Invalid history word designator `%s'" |
|---|
| 701 |
reference) |
|---|
| 702 |
(setq nth 1 mth "-$"))) |
|---|
| 703 |
(if (not mth) |
|---|
| 704 |
(if nth |
|---|
| 705 |
(setq mth nth) |
|---|
| 706 |
(setq nth 0 mth "$")) |
|---|
| 707 |
(if (string= mth "-") |
|---|
| 708 |
(setq mth (- (length textargs) 2)) |
|---|
| 709 |
(if (string= mth "*") |
|---|
| 710 |
(setq mth "$") |
|---|
| 711 |
(if (not (and (> (length mth) 1) |
|---|
| 712 |
(eq (aref mth 0) ?-))) |
|---|
| 713 |
(error "Invalid history word designator `%s'" |
|---|
| 714 |
reference) |
|---|
| 715 |
(setq mth (substring mth 1)))))) |
|---|
| 716 |
(unless (numberp nth) |
|---|
| 717 |
(setq nth (eshell-hist-word-reference nth))) |
|---|
| 718 |
(unless (numberp mth) |
|---|
| 719 |
(setq mth (eshell-hist-word-reference mth))) |
|---|
| 720 |
(cons (mapconcat 'identity (eshell-sublist textargs nth mth) "") |
|---|
| 721 |
end)))) |
|---|
| 722 |
|
|---|
| 723 |
(defun eshell-hist-parse-modifier (hist reference) |
|---|
| 724 |
"Parse a history modifier beginning for HIST in REFERENCE." |
|---|
| 725 |
(let ((here (point))) |
|---|
| 726 |
(insert reference) |
|---|
| 727 |
(prog1 |
|---|
| 728 |
(save-restriction |
|---|
| 729 |
(narrow-to-region here (point)) |
|---|
| 730 |
(goto-char (point-min)) |
|---|
| 731 |
(let ((modifiers (cdr (eshell-parse-modifiers)))) |
|---|
| 732 |
(eshell-for mod modifiers |
|---|
| 733 |
(setq hist (funcall mod hist))) |
|---|
| 734 |
hist)) |
|---|
| 735 |
(delete-region here (point))))) |
|---|
| 736 |
|
|---|
| 737 |
(defun eshell-get-next-from-history () |
|---|
| 738 |
"After fetching a line from input history, this fetches the next. |
|---|
| 739 |
In other words, this recalls the input line after the line you |
|---|
| 740 |
recalled last. You can use this to repeat a sequence of input lines." |
|---|
| 741 |
(interactive) |
|---|
| 742 |
(if eshell-save-history-index |
|---|
| 743 |
(progn |
|---|
| 744 |
(setq eshell-history-index (1+ eshell-save-history-index)) |
|---|
| 745 |
(eshell-next-input 1)) |
|---|
| 746 |
(message "No previous history command"))) |
|---|
| 747 |
|
|---|
| 748 |
(defun eshell-search-arg (arg) |
|---|
| 749 |
|
|---|
| 750 |
|
|---|
| 751 |
(if (and eshell-hist-move-to-end |
|---|
| 752 |
(< (point) eshell-last-output-end)) |
|---|
| 753 |
(goto-char eshell-last-output-end)) |
|---|
| 754 |
(cond ((or (null eshell-history-ring) |
|---|
| 755 |
(ring-empty-p eshell-history-ring)) |
|---|
| 756 |
(error "Empty input ring")) |
|---|
| 757 |
((zerop arg) |
|---|
| 758 |
|
|---|
| 759 |
|
|---|
| 760 |
(setq eshell-history-index nil) |
|---|
| 761 |
1) |
|---|
| 762 |
(t |
|---|
| 763 |
arg))) |
|---|
| 764 |
|
|---|
| 765 |
(defun eshell-search-start (arg) |
|---|
| 766 |
"Index to start a directional search, starting at `eshell-history-index'." |
|---|
| 767 |
(if eshell-history-index |
|---|
| 768 |
|
|---|
| 769 |
(mod (+ eshell-history-index (if (> arg 0) 1 -1)) |
|---|
| 770 |
(ring-length eshell-history-ring)) |
|---|
| 771 |
|
|---|
| 772 |
(if (>= arg 0) |
|---|
| 773 |
0 |
|---|
| 774 |
|
|---|
| 775 |
(1- (ring-length eshell-history-ring))))) |
|---|
| 776 |
|
|---|
| 777 |
(defun eshell-previous-input-string (arg) |
|---|
| 778 |
"Return the string ARG places along the input ring. |
|---|
| 779 |
Moves relative to `eshell-history-index'." |
|---|
| 780 |
(eshell-get-history (if eshell-history-index |
|---|
| 781 |
(mod (+ arg eshell-history-index) |
|---|
| 782 |
(ring-length eshell-history-ring)) |
|---|
| 783 |
arg))) |
|---|
| 784 |
|
|---|
| 785 |
(defun eshell-previous-input (arg) |
|---|
| 786 |
"Cycle backwards through input history." |
|---|
| 787 |
(interactive "*p") |
|---|
| 788 |
(eshell-previous-matching-input "." arg)) |
|---|
| 789 |
|
|---|
| 790 |
(defun eshell-next-input (arg) |
|---|
| 791 |
"Cycle forwards through input history." |
|---|
| 792 |
(interactive "*p") |
|---|
| 793 |
(eshell-previous-input (- arg))) |
|---|
| 794 |
|
|---|
| 795 |
(defun eshell-previous-matching-input-string (regexp arg) |
|---|
| 796 |
"Return the string matching REGEXP ARG places along the input ring. |
|---|
| 797 |
Moves relative to `eshell-history-index'." |
|---|
| 798 |
(let* ((pos (eshell-previous-matching-input-string-position regexp arg))) |
|---|
| 799 |
(if pos (eshell-get-history pos)))) |
|---|
| 800 |
|
|---|
| 801 |
(defun eshell-previous-matching-input-string-position |
|---|
| 802 |
(regexp arg &optional start) |
|---|
| 803 |
"Return the index matching REGEXP ARG places along the input ring. |
|---|
| 804 |
Moves relative to START, or `eshell-history-index'." |
|---|
| 805 |
(if (or (not (ring-p eshell-history-ring)) |
|---|
| 806 |
(ring-empty-p eshell-history-ring)) |
|---|
| 807 |
(error "No history")) |
|---|
| 808 |
(let* ((len (ring-length eshell-history-ring)) |
|---|
| 809 |
(motion (if (> arg 0) 1 -1)) |
|---|
| 810 |
(n (mod (- (or start (eshell-search-start arg)) motion) len)) |
|---|
| 811 |
(tried-each-ring-item nil) |
|---|
| 812 |
(case-fold-search (eshell-under-windows-p)) |
|---|
| 813 |
(prev nil)) |
|---|
| 814 |
|
|---|
| 815 |
(while (and (/= arg 0) (not tried-each-ring-item)) |
|---|
| 816 |
|
|---|
| 817 |
(setq prev n |
|---|
| 818 |
n (mod (+ n motion) len)) |
|---|
| 819 |
|
|---|
| 820 |
(while (and (< n len) (not tried-each-ring-item) |
|---|
| 821 |
(not (string-match regexp (eshell-get-history n)))) |
|---|
| 822 |
(setq n (mod (+ n motion) len) |
|---|
| 823 |
|
|---|
| 824 |
tried-each-ring-item (= n prev))) |
|---|
| 825 |
(setq arg (if (> arg 0) (1- arg) (1+ arg)))) |
|---|
| 826 |
|
|---|
| 827 |
|
|---|
| 828 |
(if (string-match regexp (eshell-get-history n)) |
|---|
| 829 |
n))) |
|---|
| 830 |
|
|---|
| 831 |
(defun eshell-previous-matching-input (regexp arg) |
|---|
| 832 |
"Search backwards through input history for match for REGEXP. |
|---|
| 833 |
\(Previous history elements are earlier commands.) |
|---|
| 834 |
With prefix argument N, search for Nth previous match. |
|---|
| 835 |
If N is negative, |
|---|