diff --git a/lisp/eshell/em-hist.el b/lisp/eshell/em-hist.el index fffd611c06f..df95706cbe0 100644 --- a/lisp/eshell/em-hist.el +++ b/lisp/eshell/em-hist.el @@ -195,6 +195,10 @@ eshell-hist-rebind-keys-alist (repeat :inline t sexp)) (function :tag "Command")))) +(defcustom eshell-hist-isearch nil + "Non-nil to Isearch in input history only." + :type 'boolean) + ;;; Internal Variables: (defvar eshell-history-ring nil) @@ -205,24 +209,12 @@ eshell-hist--new-items "The number of new history items that have not been written to file. This variable is local in each eshell buffer.") -(defvar-keymap eshell-isearch-map - :doc "Keymap used in isearch in Eshell." - :parent isearch-mode-map - "C-m" #'eshell-isearch-return - "C-r" #'eshell-isearch-repeat-backward - "C-s" #'eshell-isearch-repeat-forward - "C-g" #'eshell-isearch-abort - "" #'eshell-isearch-delete-char - "" #'eshell-isearch-delete-char - "C-c C-c" #'eshell-isearch-cancel) - (defvar-keymap eshell-hist-mode-map "" #'eshell-previous-matching-input-from-input "" #'eshell-next-matching-input-from-input "C-" #'eshell-previous-input "C-" #'eshell-next-input - "M-r" #'eshell-previous-matching-input - "M-s" #'eshell-next-matching-input + "M-r" #'eshell-hist-isearch-backward-regexp "C-c M-r" #'eshell-previous-matching-input-from-input "C-c M-s" #'eshell-next-matching-input-from-input "C-c C-l" #'eshell-list-history @@ -258,23 +250,12 @@ eshell-hist-initialize #'eshell-complete-history-reference nil t)) (if (and (eshell-using-module 'eshell-rebind) - (not eshell-non-interactive-p)) + (not eshell-non-interactive-p)) (let ((rebind-alist eshell-rebind-keys-alist)) (setq-local eshell-rebind-keys-alist - (append rebind-alist eshell-hist-rebind-keys-alist)) - (setq-local search-invisible t) - (setq-local search-exit-option t) - (add-hook 'isearch-mode-hook - (lambda () - (if (>= (point) eshell-last-output-end) - (setq overriding-terminal-local-map - eshell-isearch-map))) - nil t) - (add-hook 'isearch-mode-end-hook - (lambda () - (setq overriding-terminal-local-map nil)) - nil t)) + (append rebind-alist eshell-hist-rebind-keys-alist))) (eshell-hist-mode)) + (add-hook 'isearch-mode-hook #'eshell-hist--isearch-setup nil t) (make-local-variable 'eshell-history-size) (or eshell-history-size @@ -901,7 +882,7 @@ eshell-previous-matching-input (unless (minibuffer-window-active-p (selected-window)) (message "History item: %d" (- (ring-length eshell-history-ring) pos))) ;; Can't use kill-region as it sets this-command - (delete-region eshell-last-output-end (point)) + (delete-region eshell-last-output-end (point-max)) (insert-and-inherit (eshell-get-history pos))))) (defun eshell-next-matching-input (regexp arg) @@ -981,70 +962,121 @@ eshell-return-to-prompt (set-marker eshell-last-output-end orig) (goto-char eshell-last-output-end)))))) -(defun eshell-prepare-for-search () - "Make sure the old history file is at the beginning of the buffer." - (unless (get-text-property (point-min) 'history) - (save-excursion - (goto-char (point-min)) - (let ((end (copy-marker (point) t))) - (insert-file-contents eshell-history-file-name) - (set-text-properties (point-min) end - '(history t invisible t)))))) - -(defun eshell-isearch-backward (&optional invert) - "Do incremental regexp search backward through past commands." - (interactive) - (let ((inhibit-read-only t)) - (eshell-prepare-for-search) - (goto-char (point-max)) - (set-marker eshell-last-output-end (point)) - (delete-region (point) (point-max))) - (isearch-mode invert t 'eshell-return-to-prompt)) - -(defun eshell-isearch-repeat-backward (&optional invert) - "Do incremental regexp search backward through past commands." - (interactive) - (let ((old-pos (get-text-property (1- (point-max)) - 'last-search-pos))) - (when old-pos - (goto-char old-pos) - (if invert - (end-of-line) - (backward-char))) - (setq isearch-forward invert) - (isearch-search-and-update))) +(defvar-local eshell-hist--isearch-message-overlay nil) + +(defun eshell-hist--isearch-setup () + (when (and (>= (point) eshell-last-output-end) + eshell-hist-isearch) + (setq isearch-message-prefix-add "history ") + (setq-local isearch-lazy-count nil + isearch-search-fun-function #'eshell-hist--isearch-search + isearch-message-function #'eshell-hist--isearch-message + isearch-wrap-function #'eshell-hist--isearch-wrap + isearch-push-state-function #'eshell-hist--isearch-push-state) + (add-hook 'isearch-mode-end-hook #'eshell-hist--isearch-end nil t))) + +(defun eshell-hist--isearch-end () + (when (overlayp eshell-hist--isearch-message-overlay) + (delete-overlay eshell-hist--isearch-message-overlay)) + (setq isearch-message-prefix-add nil) + (setq-local isearch-search-fun-function #'isearch-search-fun-default + isearch-message-function nil + isearch-wrap-function nil + isearch-push-state-function nil) + (setq isearch-opoint (point)) + (kill-local-variable 'isearch-lazy-count) + (remove-hook 'isearch-mode-end-hook #'eshell-hist--isearch-end t) + (unless isearch-suspended + (custom-reevaluate-setting 'eshell-hist-isearch))) + +(defun eshell-hist--isearch-search () + (lambda (string bound noerror) + (let ((search-fun (isearch-search-fun-default)) + found) + (when (and bound isearch-forward (< (point) eshell-last-output-end)) + (goto-char eshell-last-output-end)) + (or + (funcall search-fun string + (if isearch-forward bound (line-beginning-position)) + noerror) + (unless bound + (condition-case nil + (progn + (while (not found) + (cond (isearch-forward + (when (or (null eshell-history-index) + (eq eshell-history-index 0)) + (error "End of history; no next item")) + (eshell-next-input 1) + (goto-char (line-beginning-position))) + (t + (when (eq eshell-history-index + (1- (ring-length eshell-history-ring))) + (error "Beginning of history; no preceding item")) + (eshell-previous-input 1) + (goto-char (point-max)))) + (setq isearch-barrier (point) isearch-opoint (point)) + (setq found (funcall search-fun string + (unless isearch-forward + (line-beginning-position)) + noerror))) + (point)) + (error nil))))))) + +(defun eshell-hist--isearch-message (&optional c-q-hack ellipsis) + (if (not (and isearch-success (not isearch-error))) + (isearch-message c-q-hack ellipsis) + (if (overlayp eshell-hist--isearch-message-overlay) + (move-overlay eshell-hist--isearch-message-overlay + (save-excursion + (goto-char (line-beginning-position)) + (forward-line 0) + (point)) + (line-beginning-position)) + (setq eshell-hist--isearch-message-overlay + (make-overlay (save-excursion + (goto-char (line-beginning-position)) + (forward-line 0) + (point)) + (line-beginning-position))) + (overlay-put eshell-hist--isearch-message-overlay 'evaporate t)) + (overlay-put eshell-hist--isearch-message-overlay + 'display (isearch-message-prefix ellipsis isearch-nonincremental)))) + +(defun eshell-hist--isearch-wrap () + (let ((pos (if isearch-forward + (1- (ring-length eshell-history-ring)) + 0))) + (setq eshell-history-index pos) + (delete-region eshell-last-output-end (point-max)) + (insert-and-inherit (eshell-get-history pos)) + (goto-char (if isearch-forward (line-beginning-position) (point-max))))) + +(defun eshell-hist--isearch-push-state () + (let ((index eshell-history-index)) + (lambda (_) + (delete-region eshell-last-output-end (point-max)) + (and index (insert-and-inherit (eshell-get-history index)))))) + +(defun eshell-hist-isearch-backward-regexp () + (interactive nil eshell-mode) + (setq eshell-hist-isearch t) + (isearch-backward-regexp nil t)) + +(defun eshell-hist-isearch-forward-regexp () + (interactive nil eshell-mode) + (setq eshell-hist-isearch t) + (isearch-forward-regexp nil t)) + +(defun eshell-isearch-backward () + (interactive nil eshell-mode) + (setq eshell-hist-isearch t) + (isearch-backward nil t)) (defun eshell-isearch-forward () - "Do incremental regexp search backward through past commands." - (interactive) - (eshell-isearch-backward t)) - -(defun eshell-isearch-repeat-forward () - "Do incremental regexp search backward through past commands." - (interactive) - (eshell-isearch-repeat-backward t)) - -(defun eshell-isearch-cancel () - (interactive) - (goto-char eshell-last-output-end) - (delete-region (point) (point-max)) - (call-interactively 'isearch-cancel)) - -(defun eshell-isearch-abort () - (interactive) - (goto-char eshell-last-output-end) - (delete-region (point) (point-max)) - (call-interactively 'isearch-abort)) - -(defun eshell-isearch-delete-char () - (interactive) - (save-excursion - (isearch-delete-char))) - -(defun eshell-isearch-return () - (interactive) - (isearch-done) - (eshell-send-input)) + (interactive nil eshell-mode) + (setq eshell-hist-isearch t) + (isearch-forward nil t)) (defun em-hist-unload-function () (remove-hook 'kill-emacs-hook 'eshell-save-some-history))