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: Sun, 10 Nov 2024 09:22:09 +0800 Message-ID: <871pzjkhmm.fsf@pengjiz.com> 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="631"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Jim Porter , Sean Whitton , James Thomas , Juri Linkov To: 74287@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Nov 10 02:23:26 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 1t9wfj-000AXA-Uc for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 10 Nov 2024 02:23:24 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t9wfU-0004J7-TC; Sat, 09 Nov 2024 20:23:09 -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 1t9wfQ-0004Ii-Ik for bug-gnu-emacs@gnu.org; Sat, 09 Nov 2024 20:23:06 -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 1t9wfQ-0005WK-9Z for bug-gnu-emacs@gnu.org; Sat, 09 Nov 2024 20:23:04 -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:From:To:Subject; bh=lVbyOeC+C4vLy+xAzHW52zud3RzqlfywVthoAxVD9ng=; b=Ju2GeJXngdpvExFbuhjsF4jTrL/xcqZuue40MrfisTf6aUB3wVxXrOkTPhvY3USyMkjGc6jEv6j5k2FfoAA5PLNKPTBeTo3bpCD46OIddfDKGGbx0XWNt65UW2gCoRWaj9mfh1QUyzERb1iwF9F+HPI8lXpnZTQYEJl/LxaWLYHDUKG6EoyjhtbfrVHvWfxQuGssTBfSt9MtSjIyoXFejwYNXsmr9TnbFfTfnNtuhhbM6Dg2/PDKyEphbVODH++WVq4yRs6UrStpD0jGai0oWVwxsp8yG+N9h4qbG42XHi0ek8fzRe8NW1GXMwVEZLEqBg9h0vOk0vKYfkluokK7lA==; Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1t9wfP-0002Jz-4O for bug-gnu-emacs@gnu.org; Sat, 09 Nov 2024 20:23:03 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Pengji Zhang Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 10 Nov 2024 01:23:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 74287 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.17312017678886 (code B ref -1); Sun, 10 Nov 2024 01:23:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 10 Nov 2024 01:22:47 +0000 Original-Received: from localhost ([127.0.0.1]:54988 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1t9wf5-0002JC-T3 for submit@debbugs.gnu.org; Sat, 09 Nov 2024 20:22:46 -0500 Original-Received: from lists.gnu.org ([209.51.188.17]:47482) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1t9wf2-0002J2-2B for submit@debbugs.gnu.org; Sat, 09 Nov 2024 20:22:41 -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 1t9wf0-0004HJ-Ei for bug-gnu-emacs@gnu.org; Sat, 09 Nov 2024 20:22:38 -0500 Original-Received: from fhigh-b3-smtp.messagingengine.com ([202.12.124.154]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1t9wex-0005Vn-9q for bug-gnu-emacs@gnu.org; Sat, 09 Nov 2024 20:22:38 -0500 Original-Received: from phl-compute-08.internal (phl-compute-08.phl.internal [10.202.2.48]) by mailfhigh.stl.internal (Postfix) with ESMTP id 21A4425400F2; Sat, 9 Nov 2024 20:22:34 -0500 (EST) Original-Received: from phl-mailfrontend-02 ([10.202.2.163]) by phl-compute-08.internal (MEProxy); Sat, 09 Nov 2024 20:22:34 -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 :message-id:mime-version:reply-to:subject:subject:to:to; s=fm1; t=1731201753; x=1731288153; bh=lVbyOeC+C4vLy+xAzHW52zud3Rzqlfyw VthoAxVD9ng=; b=EzzXuDGAlEPWN5u3i/SdyFz7Ca4u7aAe0mKO0qFVxIjFoLHI AiN+P/zqXZVuxhQYaM8vqSyvVXpB9m7vKomZyUVvFIlUPyCLOfmy+6VYm+/Lyfox 1EWcfx0xMWJsh1LDIPjhHkeljNyjME4QWTt+YCUt2W8nbb5814GIeASJvWS89P8W uoKDOSxy0ChA6NixXkp8P2EKQQ+w7jpQ6odXmXm3sWIlR3X1NycUHGGR1RmR+abF GF8MookU/mO4VzaQicWGIUYTJB61eN8ZAhxM+/X7D5zX5qs5k6GUPxexMzpovRm8 55N3brh2+wSgPVkyx/pAr3qxXgphRgOW1+kEpw== 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:message-id :mime-version:reply-to:subject:subject:to:to:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t=1731201753; x= 1731288153; bh=lVbyOeC+C4vLy+xAzHW52zud3RzqlfywVthoAxVD9ng=; b=P 5DyAY8FzT8IWET2LxwyN9PNgDTL3d4ZUgu/GUYjdiugYzg2tuIVJShiVwu+32KmH gledpOOTohsHLDfwHCx6cE7FXSeZdAf66lC10ZkII9h3rNO4VUMtgixdwleyo2lZ 2vcEDd0pJ118sUQZFP/FKyppp+wfbOapX7Y6x9F75/QTozeomf4wp49tygLINC/X 4irmOG367S9ZCmzRqtzcuoshT/y1ouDuPzgPURbCz2BtgZbLL8eJ7LlRW8Kfvf+F xynjNl9Y9ZSUsyR5yWI1B9aKmjgveR0ntImztzN/Us2ZwKsZ1I1x0e+fe7+XFYtu LCr1jwqs8AORQYuXH6hoQ== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefuddrtdelgdefgecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpggftfghnshhusghstghrihgsvgdpuffr tefokffrpgfnqfghnecuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnth hsucdlqddutddtmdenucfjughrpefhvfevufffkfggtgesmhdtreertddttdenucfhrhho mheprfgvnhhgjhhiucgkhhgrnhhguceomhgvsehpvghnghhjihiirdgtohhmqeenucggtf frrghtthgvrhhnpeeugedtkeejtefgleeuffeljeffuddtvddvleefffelieekteelgfdt fefhkeeikeenucffohhmrghinhepghhnuhdrohhrghenucevlhhushhtvghrufhiiigvpe dtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehmvgesphgvnhhgjhhiiidrtghomhdpnhgs pghrtghpthhtohephedpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepsghughdqgh hnuhdqvghmrggtshesghhnuhdrohhrghdprhgtphhtthhopehjphhorhhtvghrsghughhs sehgmhgrihhlrdgtohhmpdhrtghpthhtohepjhhurhhisehlihhnkhhovhdrnhgvthdprh gtphhtthhopehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgdprhgtphht thhopehjihhmjhhovg X-ME-Proxy: Feedback-ID: i16614472:Fastmail Original-Received: by mail.messagingengine.com (Postfix) with ESMTPA; Sat, 9 Nov 2024 20:22:32 -0500 (EST) Received-SPF: pass client-ip=202.12.124.154; envelope-from=me@pengjiz.com; helo=fhigh-b3-smtp.messagingengine.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action 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:295162 Archived-At: --=-=-= Content-Type: text/plain Hello, This patch brings a comint-like interface for history Isearch to Eshell. To try it, type 'M-r' in Eshell, and search through the input history ring incrementally. Compared to the existing implementation, this patch integrates with Isearch properly, like what we do for the minibuffer and comint modes. There are relevant discussions (thank you all for the feedback!) in the mailing list: https://lists.gnu.org/archive/html/emacs-devel/2024-11/msg00069.html For Jim's concern, I do not think this implementation closes the door for a history completion interface, as shown by Sean and Juri. Thanks! Pengji --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Rework-history-Isearch-for-Eshell.patch >From d455ec1bf1b49871cf866457df77f6ab45f5623f Mon Sep 17 00:00:00 2001 From: Pengji Zhang Date: Sun, 10 Nov 2024 08:50:27 +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..de986b05f48 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-messasge-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-messasge-overlay) + (delete-overlay eshell--history-isearch-messasge-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-messasge-overlay) + (move-overlay eshell--history-isearch-messasge-overlay + (save-excursion + (goto-char eshell-last-output-end) + (forward-line 0) + (point)) + eshell-last-output-end) + (setq eshell--history-isearch-messasge-overlay + (make-overlay (save-excursion + (goto-char eshell-last-output-end) + (forward-line 0) + (point)) + eshell-last-output-end)) + (overlay-put eshell--history-isearch-messasge-overlay 'evaporate t)) + (overlay-put eshell--history-isearch-messasge-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.0 --=-=-=--