From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Pengji Zhang Newsgroups: gmane.emacs.bugs Subject: bug#74287: [PATCH] Rework history Isearch for Eshell Date: Thu, 05 Dec 2024 20:43:02 +0800 Message-ID: <87jzcecnc9.fsf@pengjiz.com> References: <871pzjkhmm.fsf@pengjiz.com> <8c80ae91-476e-e665-43be-86d72fb57ed1@gmail.com> <86v7waz603.fsf@gmx.net> <6fdd8386-8915-e3a8-046a-b18750a7057d@gmail.com> <87v7vzgtuq.fsf@mail.linkov.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="2051"; mail-complaints-to="usenet@ciao.gmane.io" Cc: 74287@debbugs.gnu.org To: Juri Linkov , Jim Porter Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Dec 05 13:44:22 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1tJBDR-0000LS-JK for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 05 Dec 2024 13:44:22 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tJBDA-0007UH-Hf; Thu, 05 Dec 2024 07:44:04 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tJBD8-0007Ty-Li for bug-gnu-emacs@gnu.org; Thu, 05 Dec 2024 07:44:02 -0500 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tJBD8-0004rC-Bh for bug-gnu-emacs@gnu.org; Thu, 05 Dec 2024 07:44:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:References:In-Reply-To:From:To:Subject; bh=uODlpwywhMR2AUsiB8YurItobRKPG43vIq0Y2FQ5ctI=; b=H0M7D+c6fEsDncv4OWlI6C2NM4wmRuS92HgkGiBPq1diuTK96R7f0akheIi05CQgquftwlo9szjwGDWnYoJ9dHnz9+tI31KKrSoNG/R3Oga4N4oyEjEvLEbe0W6ctPAz4GPLJVu24+lT7hZ/X21LXqTJNznuKjVVDnguP61ns9tt7Cqdoa4Y4e03rJJToUvJ6TZytPfnBCRjrAZDioBScl+GEvYHAgeBRjjmklpuPA8zluG+YSMsi+W/BfOcy8vvizYoZGzEeu4bGPfXMfTElJcuynaR4vsZzbVIXMGRgradsFu7rhPxQTXol9bvpTnF2FmX84Oe7TRhVkWQa+UYnA==; Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tJBD7-0002kV-Si for bug-gnu-emacs@gnu.org; Thu, 05 Dec 2024 07:44:01 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Pengji Zhang Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 05 Dec 2024 12:44:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 74287 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 74287-submit@debbugs.gnu.org id=B74287.173340259810500 (code B ref 74287); Thu, 05 Dec 2024 12:44:01 +0000 Original-Received: (at 74287) by debbugs.gnu.org; 5 Dec 2024 12:43:18 +0000 Original-Received: from localhost ([127.0.0.1]:38608 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tJBCP-0002jF-Hy for submit@debbugs.gnu.org; Thu, 05 Dec 2024 07:43:18 -0500 Original-Received: from fhigh-b2-smtp.messagingengine.com ([202.12.124.153]:38917) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tJBCM-0002iy-04 for 74287@debbugs.gnu.org; Thu, 05 Dec 2024 07:43:16 -0500 Original-Received: from phl-compute-11.internal (phl-compute-11.phl.internal [10.202.2.51]) by mailfhigh.stl.internal (Postfix) with ESMTP id 98EF92540227; Thu, 5 Dec 2024 07:43:08 -0500 (EST) Original-Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-11.internal (MEProxy); Thu, 05 Dec 2024 07:43:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pengjiz.com; h= cc:cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1733402588; x=1733488988; bh=uODlpwywhM R2AUsiB8YurItobRKPG43vIq0Y2FQ5ctI=; b=BV+dtusfAhko/mY28H0DawLFgu SyGpYxVExEyjXXNg5YmTfm8M1eZ4neKq5SlxM1/kVs80ANh9VntAzD970gbTFLCp 35imaPwXnYIIpKInoobCPNsbGrvydFLgN87cN77ipPR2ju6KcedN1uX4YUSwoqjZ QCRXjDK+lnHEovoGGwI8XdjI0QfXrGxqZ3IyfHHb3Qhz0jv51XOPxQ9kpqmZAM5e vUahOVHpRhI4HMp2GYZS6XBplFc3mWHjpI5kLzFpOW3PpEW211g8PM7ImmdO0nkx Z9iX1YyV+QlXZ9QBH1zhAUfmBmfS3Ni41f4G7FDu/CVVW8VXjbuq9K9AiTTw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1733402588; x=1733488988; bh=uODlpwywhMR2AUsiB8YurItobRKPG43vIq0 Y2FQ5ctI=; b=L7RBmXtimzuEHR32AQKEMFTLKHcs7J7m3Sudwj4p1Vai+JNpmZm 1pY9j3PkoGyJGvs1gM75C4L+fYWOl882wwFrL7K+cyoDXtJJoPSxi5BYFYpaL6YY 3GuD0+ZSazaTy+eQil7VXwzxW4VQxqRqeB35vD6fJhakCnKnyLIuzdOGZKJxiDg5 Pr+P+VjEGM0AJJVrVNRlLq2CrT97W8uzms31EpwTuQmLe5iFVX17WDtGnja3v/4b 2dM3Iz/ma9Jk73vRAGJU2abS+eHuazJVXRJ+vPiwBjRHQH1hd1xosdLBy9NQTj8f UQU1ccXW+fD63DOnENwZFI4M+lcv1Mu3woA== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefuddrieejgdegvdcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpggftfghnshhusghstghrihgsvgdpuffr tefokffrpgfnqfghnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnth hsucdlqddutddtmdenucfjughrpefhvfevufgjfhffkfggtgesmhdtreertddttdenucfh rhhomheprfgvnhhgjhhiucgkhhgrnhhguceomhgvsehpvghnghhjihiirdgtohhmqeenuc ggtffrrghtthgvrhhnpeeifffgieeulefgjeejiedvleehffffueeiiefhhfffveejuefh gedvhfekgfelkeenucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfh hrohhmpehmvgesphgvnhhgjhhiiidrtghomhdpnhgspghrtghpthhtohepfedpmhhouggv pehsmhhtphhouhhtpdhrtghpthhtohepjhhurhhisehlihhnkhhovhdrnhgvthdprhgtph htthhopehjphhorhhtvghrsghughhssehgmhgrihhlrdgtohhmpdhrtghpthhtohepjeeg vdekjeesuggvsggsuhhgshdrghhnuhdrohhrgh X-ME-Proxy: Feedback-ID: i16614472:Fastmail Original-Received: by mail.messagingengine.com (Postfix) with ESMTPA; Thu, 5 Dec 2024 07:43:06 -0500 (EST) In-Reply-To: <87v7vzgtuq.fsf@mail.linkov.net> X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:296471 Archived-At: --=-=-= Content-Type: text/plain Juri Linkov writes: > (There is only one typo in the variable name > 'eshell--history-isearch-messasge-overlay'.) Thanks! Corrected in the attached updated patch. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Rework-history-Isearch-for-Eshell.patch Content-Description: Updated patch >From 9df1d8eaf626c396b88fe616a6e89d5a8e7b963c Mon Sep 17 00:00:00 2001 From: Pengji Zhang Date: Thu, 5 Dec 2024 20:25:25 +0800 Subject: [PATCH] Rework history Isearch for Eshell This is to make history Isearch for Eshell similar to that of 'comint-mode', by hooking into Isearch properly instead of defining new commands to emulate Isearch. * lisp/eshell/em-hist.el (eshell-history-isearch): New user option. (eshell-goto-history, eshell--isearch-setup) (eshell-history-isearch-end, eshell-history-isearch-search) (eshell-history-isearch-message, eshell-history-isearch-wrap) (eshell-history-isearch-push-state): New functions. (eshell-isearch-backward-regexp, eshell-isearch-forward-regexp): New commands. (eshell--history-isearch-messasge-overlay) (eshell--stored-incomplete-input): New internal variables. (eshell-hist-mode-map): Bind 'M-r' to 'eshell-isearch-backward-regexp' and free 'M-s' binding for normal in-buffer search commands. (eshell-isearch-backward, eshell-isearch-forward): Use the new way to start searching. (eshell-hist-initialize): Use the new Isearch setup function. (eshell-previous-matching-input): Use 'eshell-goto-history'. Also inhibit messages when searching. (eshell-isearch-map, eshell-isearch-repeat-backward) (eshell-isearch-abort, eshell-isearch-delete-char) (eshell-isearch-return, eshell-isearch-cancel) (eshell-isearch-repeat-forward, eshell-test-imatch) (eshell-return-to-prompt, eshell-prepare-for-search): Remove. These are for the old history Isearch implementation. * doc/misc/eshell.texi (History): Document changes. * etc/NEWS: Annouce changes. --- doc/misc/eshell.texi | 15 +- etc/NEWS | 23 +++ lisp/eshell/em-hist.el | 322 ++++++++++++++++++++++++----------------- 3 files changed, 223 insertions(+), 137 deletions(-) diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index ee4d0ca09c8..701137ea1b4 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -2661,10 +2661,10 @@ History @table @kbd @kindex M-r -@kindex M-s @item M-r -@itemx M-s -History I-search. +History I-search. @kbd{M-r} starts an incremental search in input +history. While searching, type @kbd{C-r} to move to the previous match, +and @kbd{C-s} to move to the next match in the input history. @kindex M-p @kindex M-n @@ -2675,6 +2675,15 @@ History previous or next line that begins with that string. @end table +@vindex eshell-history-isearch +If you would like to use the default Isearch key-bindings to search +through input history, you may customize @code{eshell-history-isearch} +to @code{t}. That makes, for example, @kbd{C-r} and @kbd{C-M-r} in an +Eshell buffer search in input history only. In addition, if the value +of @code{eshell-history-isearch} is @code{dwim}, those commands search +in the history when the point is after the last prompt, and search in +the buffer when the point is before or within the last prompt. + @node Extension modules @chapter Extension modules Eshell provides a facility for defining extension modules so that they diff --git a/etc/NEWS b/etc/NEWS index d1c7303f976..c1df6f78d9f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -288,6 +288,29 @@ This hook runs after an Eshell session has been fully initialized, immediately before running 'eshell-post-command-hook' for the first time. ++++ +*** Improved history Isearch. +History Isearch in Eshell is reworked. Two new commands +'eshell-isearch-backward-regexp' and 'eshell-isearch-forward-regexp' are +added for incrementally searching through the input history. +'eshell-isearch-backward-regexp' is bound to 'M-r' by default, and 'M-s' +is freed for normal search commands. If you would like to restore the +previous key-bindings for the non-incremental search commands, put in +your configuration: + + (keymap-set eshell-hist-mode-map "M-r" + #'eshell-previous-matching-input) + (keymap-set eshell-hist-mode-map "M-s" + #'eshell-next-matching-input) + ++++ +*** New user option 'eshell-history-isearch' +When 'eshell-history-isearch' is nil (the default), Isearch commands +search in the buffer contents. If you customize it to t, those commands +only search in input history. If you customize it to the symbol 'dwim', +those commands search in input history only when the point is after the +last prompt. + ** SHR +++ diff --git a/lisp/eshell/em-hist.el b/lisp/eshell/em-hist.el index fffd611c06f..c85e7704eca 100644 --- a/lisp/eshell/em-hist.el +++ b/lisp/eshell/em-hist.el @@ -34,7 +34,6 @@ ;; Also, most of `comint-mode's keybindings are accepted: ;; ;; M-r ; search backward for a previous command by regexp -;; M-s ; search forward for a previous command by regexp ;; M-p ; access the last command entered, repeatable ;; M-n ; access the first command entered, repeatable ;; @@ -132,6 +131,17 @@ eshell-input-filter (function :tag "Other function")) :risky t) +(defcustom eshell-history-isearch nil + "Non-nil to Isearch in input history only. +If t, usual Isearch keys like \\[isearch-forward] in Eshell search in +the input history only. If `dwim', Isearch in the input history when +point is at the command line, otherwise search in the current Eshell +buffer." + :type '(choice (const :tag "Don't search in input history" nil) + (const :tag "Search histroy when point is on command line" dwim) + (const :tag "Always search in input history" t)) + :version "31.1") + (defun eshell-hist--update-keymap (symbol value) "Update `eshell-hist-mode-map' for `eshell-hist-match-partial'." ;; Don't try to set this before it is bound. See below. @@ -204,25 +214,17 @@ eshell-save-history-index (defvar eshell-hist--new-items nil "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-local eshell--history-isearch-message-overlay nil + "Overlay for Isearch message when searching through input history.") +(defvar-local eshell--stored-incomplete-input nil + "Stored input for history cycling.") (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-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 @@ -261,20 +263,9 @@ eshell-hist-initialize (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--isearch-setup nil t) (make-local-variable 'eshell-history-size) (or eshell-history-size @@ -384,6 +375,23 @@ eshell-get-history "Get an input line from the history ring." (ring-ref (or ring eshell-history-ring) index)) +(defun eshell-goto-history (pos) + "Replace command line with the element at POS of history ring. +Also update `eshell-history-index'. As a special case, if POS is nil +and `eshell--stored-incomplete-input' is a non-empty string, restore the +saved input." + (when (null eshell-history-index) + (setq eshell--stored-incomplete-input + (buffer-substring-no-properties eshell-last-output-end + (point-max)))) + (setq eshell-history-index pos) + ;; Can't use kill-region as it sets this-command + (delete-region eshell-last-output-end (point-max)) + (if (and pos (not (ring-empty-p eshell-history-ring))) + (insert-and-inherit (eshell-get-history pos)) + (when (> (length eshell--stored-incomplete-input) 0) + (insert-and-inherit eshell--stored-incomplete-input)))) + (defun eshell-add-input-to-history (input) "Add the string INPUT to the history ring. Input is entered into the input history ring, if the value of @@ -897,12 +905,12 @@ eshell-previous-matching-input ;; Has a match been found? (if (null pos) (error "Not found") - (setq eshell-history-index pos) - (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)) - (insert-and-inherit (eshell-get-history pos))))) + (eshell-goto-history pos) + (unless (or (minibuffer-window-active-p (selected-window)) + ;; No messages for Isearch because it will show the + ;; same messages (and more). + isearch-mode) + (message "History item: %d" (- (ring-length eshell-history-ring) pos)))))) (defun eshell-next-matching-input (regexp arg) "Search forwards through input history for match for REGEXP. @@ -937,114 +945,160 @@ eshell-next-matching-input-from-input (interactive "p") (eshell-previous-matching-input-from-input (- arg))) -(defun eshell-test-imatch () - "If isearch match good, put point at the beginning and return non-nil." - (if (get-text-property (point) 'history) - (progn (beginning-of-line) t) - (let ((before (point))) - (beginning-of-line) - (if (and (not (bolp)) - (<= (point) before)) - t - (if isearch-forward - (progn - (end-of-line) - (forward-char)) - (beginning-of-line) - (backward-char)))))) - -(defun eshell-return-to-prompt () - "Once a search string matches, insert it at the end and go there." - (setq isearch-other-end nil) - (let ((found (eshell-test-imatch)) before) - (while (and (not found) - (setq before - (funcall (if isearch-forward - 're-search-forward - 're-search-backward) - isearch-string nil t))) - (setq found (eshell-test-imatch))) - (if (not found) - (progn - (goto-char eshell-last-output-end) - (delete-region (point) (point-max))) - (setq before (point)) - (let ((text (buffer-substring-no-properties - (point) (line-end-position))) - (orig (marker-position eshell-last-output-end))) - (goto-char eshell-last-output-end) - (delete-region (point) (point-max)) - (when (and text (> (length text) 0)) - (insert text) - (put-text-property (1- (point)) (point) - 'last-search-pos before) - (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-setup () + "Set up Isearch to search the input history. +Intended to be added to `isearch-mode-hook' in an Eshell buffer." + (when (and + ;; Eshell is busy running a foreground process + (not eshell-foreground-command) + (or (eq eshell-history-isearch t) + (and (eq eshell-history-isearch 'dwim) + (>= (point) eshell-last-output-end)))) + (setq isearch-message-prefix-add "history ") + (setq-local isearch-lazy-count nil) + (setq-local isearch-search-fun-function #'eshell-history-isearch-search + isearch-message-function #'eshell-history-isearch-message + isearch-wrap-function #'eshell-history-isearch-wrap + isearch-push-state-function #'eshell-history-isearch-push-state) + (add-hook 'isearch-mode-end-hook #'eshell-history-isearch-end nil t))) + +(defun eshell-history-isearch-end () + "Clean up after terminating history Isearch." + (when (overlayp eshell--history-isearch-message-overlay) + (delete-overlay eshell--history-isearch-message-overlay)) + (setq isearch-message-prefix-add nil) + (kill-local-variable 'isearch-lazy-count) + (setq-local isearch-search-fun-function #'isearch-search-fun-default + isearch-message-function nil + isearch-wrap-function nil + isearch-push-state-function nil) + (remove-hook 'isearch-mode-end-hook #'eshell-history-isearch-end t) + (setq isearch-opoint (point)) + (unless isearch-suspended + (custom-reevaluate-setting 'eshell-history-isearch))) + +(defun eshell-history-isearch-search () + "Return search function for Isearch in input history." + (lambda (string bound noerror) + (let ((search-fun (isearch-search-fun-default)) + (found nil)) + ;; Avoid highlighting matches in and before the last prompt + (when (and bound isearch-forward + (< (point) eshell-last-output-end)) + (goto-char eshell-last-output-end)) + (or + ;; First search in the initial input + (funcall search-fun string + (if isearch-forward bound eshell-last-output-end) + noerror) + ;; Then search in the input history: put next/previous history + ;; element in the command line successively, then search the + ;; string in the command line. Do this only when not + ;; lazy-highlighting (`bound' is nil). + (unless bound + (condition-case nil + (progn + (while (not found) + (cond (isearch-forward + ;; Signal an error explicitly to break + (when (or (null eshell-history-index) + (eq eshell-history-index 0)) + (error "End of history; no next item")) + (eshell-next-input 1) + (goto-char eshell-last-output-end)) + (t + ;; Signal an error explicitly to break + (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)) + ;; After putting an history element in the command + ;; line, search the string in them. + (setq found (funcall search-fun string + (unless isearch-forward + eshell-last-output-end) + noerror))) + (point)) + ;; Return when no next/preceding element error signaled + (error nil))))))) + +(defun eshell-history-isearch-message (&optional c-q-hack ellipsis) + "Display the input history search prompt. +If there are no search errors, this function displays an overlay with +the Isearch prompt which replaces the original Eshell prompt. +Otherwise, it displays the standard Isearch message returned from the +function `isearch-message'." + (if (not (and isearch-success (not isearch-error))) + ;; Use standard message function (which displays a message in the + ;; echo area) when not in command line, or search fails or has + ;; errors (like incomplete regexp). + (isearch-message c-q-hack ellipsis) + ;; Otherwise, use an overlay over the Eshell prompt. + (if (overlayp eshell--history-isearch-message-overlay) + (move-overlay eshell--history-isearch-message-overlay + (save-excursion + (goto-char eshell-last-output-end) + (forward-line 0) + (point)) + eshell-last-output-end) + (setq eshell--history-isearch-message-overlay + (make-overlay (save-excursion + (goto-char eshell-last-output-end) + (forward-line 0) + (point)) + eshell-last-output-end)) + (overlay-put eshell--history-isearch-message-overlay 'evaporate t)) + (overlay-put eshell--history-isearch-message-overlay + 'display (isearch-message-prefix ellipsis + isearch-nonincremental)) + (if (and eshell-history-index (not ellipsis)) + (message "History item: %d" (- (ring-length eshell-history-ring) + eshell-history-index)) + (message "")))) + +(defun eshell-history-isearch-wrap () + "Wrap the input history search." + (if isearch-forward + (eshell-goto-history (1- (ring-length eshell-history-ring))) + (eshell-goto-history nil)) + (goto-char (if isearch-forward eshell-last-output-end (point-max)))) + +(defun eshell-history-isearch-push-state () + "Save a function restoring the state of input history search. +Save `eshell-history-index' to the additional state parameter in the +search status stack." + (let ((index eshell-history-index)) + (lambda (_cmd) + (eshell-goto-history index)))) (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))) + "Do incremental search backward through past commands." + (interactive nil eshell-mode) + (setq eshell-history-isearch t) + (if invert + (isearch-forward nil t) + (isearch-backward nil t))) (defun eshell-isearch-forward () - "Do incremental regexp search backward through past commands." - (interactive) + "Do incremental search forward through past commands." + (interactive nil eshell-mode) (eshell-isearch-backward t)) -(defun eshell-isearch-repeat-forward () +(defun eshell-isearch-backward-regexp (&optional invert) "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-history-isearch t) + (if invert + (isearch-forward-regexp nil t) + (isearch-backward-regexp nil t))) + +(defun eshell-isearch-forward-regexp () + "Do incremental regexp search forward through past commands." + (interactive nil eshell-mode) + (eshell-isearch-backward-regexp t)) (defun em-hist-unload-function () (remove-hook 'kill-emacs-hook 'eshell-save-some-history)) -- 2.47.1 --=-=-=--