Integrate Isearch with line mode input history
* eat.el (eat-line-input-history-isearch): New user option. * eat.el (eat-line-mode-map): Bind 'M-r' to 'eat-line-history-isearch-backward-regexp'. * eat.el (eat-line-mode): Error if process isn't running. * eat.el (eat--line-history-isearch-message-overlay) (eat--saved-line-input-history-isearch): New variable. * eat.el (eat--line-delete-input): Remove. All callers changed. * eat.el (eat-line-history-isearch-backward-regexp) (eat-line-history-isearch-backward): New command. * eat.el (eat--line-history-isearch-setup) (eat--line-history-isearch-end, eat--line-history-isearch-wrap) (eat--line-history-isearch-search, eat--line-goto-input) (eat--line-history-isearch-message) (eat--line-history-isearch-push-state) (eat--line-history-isearch-pop-state): New function. * eat.el (eat-mode): Make 'eat--line-history-isearch-message-overlay', 'isearch-search-fun-function', 'isearch-message-function', 'isearch-wrap-function' and 'isearch-push-state-function' buffer-local. Add 'eat--line-history-isearch-setup' to 'isearch-mode-hook' locally. * eat.texi (Line Mode): Document this new feature.
This commit is contained in:
parent
a81ccab99a
commit
fe9a128d6c
2 changed files with 260 additions and 15 deletions
248
eat.el
248
eat.el
|
@ -215,6 +215,22 @@ end of the current logical (not visual) line after insertion."
|
|||
(const :tag "Move to end of line" end-of-line))
|
||||
:group 'eat-ui)
|
||||
|
||||
(defcustom eat-line-input-history-isearch nil
|
||||
"Non-nil to Isearch in input history only, not in the terminal.
|
||||
|
||||
If t, usual Isearch keys like \\[isearch-backward] and \
|
||||
\\[isearch-backward-regexp] in Eat buffer search in
|
||||
the input history. If `dwim', Isearch keys search in the input
|
||||
history only when initial point position is on input line. When
|
||||
starting Isearch from other parts of the Eat buffer, they search in
|
||||
the Eat buffer. If nil, Isearch operates on the whole Eat buffer."
|
||||
:type '(choice (const :tag "Don't search in input history" nil)
|
||||
(const :tag "When point is on input line initially, \
|
||||
search history"
|
||||
dwim)
|
||||
(const :tag "Always search in input history" t))
|
||||
:group 'eat-ui)
|
||||
|
||||
(defcustom eat-line-input-send-function #'eat-line-send-default
|
||||
"Function to send the shell prompt input.
|
||||
|
||||
|
@ -5710,7 +5726,8 @@ EVENT is the mouse event."
|
|||
(define-key map [?\M-n] #'eat-line-next-input)
|
||||
(define-key map [C-up] #'eat-line-previous-input)
|
||||
(define-key map [C-down] #'eat-line-next-input)
|
||||
(define-key map [?\M-r] #'eat-line-previous-matching-input)
|
||||
(define-key map [?\M-r]
|
||||
#'eat-line-history-isearch-backward-regexp)
|
||||
(define-key map [?\C-c ?\C-r] #'eat-line-find-input)
|
||||
(define-key map [?\C-c ?\M-r]
|
||||
#'eat-line-previous-matching-input-from-input)
|
||||
|
@ -5846,6 +5863,8 @@ MODE should one of:
|
|||
(defun eat-line-mode ()
|
||||
"Switch to line mode."
|
||||
(interactive)
|
||||
(unless eat--terminal
|
||||
(error "Process not running"))
|
||||
(eat--line-mode +1)
|
||||
(eat--semi-char-mode -1)
|
||||
(eat--char-mode -1)
|
||||
|
@ -5955,6 +5974,12 @@ character."
|
|||
(defvar eat--line-matching-input-from-input-string ""
|
||||
"Input previously used to match input history.")
|
||||
|
||||
(defvar eat--line-history-isearch-message-overlay nil
|
||||
"Overlay to show Isearch prompt, replacing the shell prompt.")
|
||||
|
||||
(defvar eat--saved-line-input-history-isearch 'not-saved
|
||||
"Saved value of `eat-line-input-history-isearch'.")
|
||||
|
||||
(defun eat--line-reset-input-ring-vars ()
|
||||
"Reset variable after a new shell prompt."
|
||||
(setq eat--line-input-ring-index nil)
|
||||
|
@ -6011,7 +6036,7 @@ character."
|
|||
"Restore unfinished input."
|
||||
(interactive)
|
||||
(when eat--line-input-ring-index
|
||||
(eat--line-delete-input)
|
||||
(delete-region (eat-term-end eat--terminal) (point-max))
|
||||
(when (> (length eat--line-stored-incomplete-input) 0)
|
||||
(insert eat--line-stored-incomplete-input)
|
||||
(message "Input restored"))
|
||||
|
@ -6099,11 +6124,6 @@ Moves relative to START, or `eat--line-input-ring-index'."
|
|||
(when (string-match regexp (ring-ref eat--line-input-ring n))
|
||||
n)))
|
||||
|
||||
(defun eat--line-delete-input ()
|
||||
"Delete all input between accumulation or process mark and point."
|
||||
;; Can't use kill-region as it sets `this-command'.
|
||||
(delete-region (eat-term-end eat--terminal) (point-max)))
|
||||
|
||||
(defun eat-line-previous-matching-input (regexp n &optional restore)
|
||||
"Search backwards through input history for match for REGEXP.
|
||||
|
||||
|
@ -6136,7 +6156,7 @@ If RESTORE is non-nil, restore input in case of wrap."
|
|||
(unless isearch-mode
|
||||
(let ((message-log-max nil)) ; Do not write to *Messages*.
|
||||
(message "History item: %d" (1+ pos))))
|
||||
(eat--line-delete-input)
|
||||
(delete-region (eat-term-end eat--terminal) (point-max))
|
||||
(insert (ring-ref eat--line-input-ring pos))))))
|
||||
|
||||
(defun eat-line-next-matching-input (regexp n)
|
||||
|
@ -6198,9 +6218,213 @@ If N is negative, search backwards for the -Nth previous match."
|
|||
(cl-incf i))
|
||||
(when pos
|
||||
(setq eat--line-input-ring-index pos))
|
||||
(eat--line-delete-input)
|
||||
(delete-region (eat-term-end eat--terminal) (point-max))
|
||||
(insert str)))
|
||||
|
||||
(defun eat-line-history-isearch-backward ()
|
||||
"Search for a string backward in input history using Isearch."
|
||||
(interactive)
|
||||
(setq eat--saved-line-input-history-isearch
|
||||
eat-line-input-history-isearch)
|
||||
(setq eat-line-input-history-isearch t)
|
||||
(isearch-backward nil t))
|
||||
|
||||
(defun eat-line-history-isearch-backward-regexp ()
|
||||
"Search for a regular expression backward in input history using Isearch."
|
||||
(interactive)
|
||||
(setq eat--saved-line-input-history-isearch
|
||||
eat-line-input-history-isearch)
|
||||
(setq eat-line-input-history-isearch t)
|
||||
(isearch-backward-regexp nil t))
|
||||
|
||||
(defun eat--line-history-isearch-setup ()
|
||||
"Set up Eat buffer for using Isearch to search the input history."
|
||||
(when (or (eq eat-line-input-history-isearch t)
|
||||
(and (eq eat-line-input-history-isearch 'dwim)
|
||||
(>= (point) (eat-term-end eat--terminal))))
|
||||
(setq isearch-message-prefix-add "history ")
|
||||
(setq isearch-search-fun-function
|
||||
#'eat--line-history-isearch-search)
|
||||
(setq isearch-message-function
|
||||
#'eat--line-history-isearch-message)
|
||||
(setq isearch-wrap-function #'eat--line-history-isearch-wrap)
|
||||
(setq isearch-push-state-function
|
||||
#'eat--line-history-isearch-push-state)
|
||||
(make-local-variable 'isearch-lazy-count)
|
||||
(setq isearch-lazy-count nil)
|
||||
(add-hook 'isearch-mode-end-hook
|
||||
'eat--line-history-isearch-end nil t)))
|
||||
|
||||
(defun eat--line-history-isearch-end ()
|
||||
"Clean up after terminating Isearch."
|
||||
(when eat--line-history-isearch-message-overlay
|
||||
(delete-overlay eat--line-history-isearch-message-overlay))
|
||||
(setq isearch-message-prefix-add nil)
|
||||
(setq isearch-search-fun-function 'isearch-search-fun-default)
|
||||
(setq isearch-message-function nil)
|
||||
(setq isearch-wrap-function nil)
|
||||
(setq isearch-push-state-function nil)
|
||||
;; Force isearch to not change mark.
|
||||
(setq isearch-opoint (point))
|
||||
(kill-local-variable 'isearch-lazy-count)
|
||||
(remove-hook 'isearch-mode-end-hook
|
||||
'eat--line-history-isearch-end t)
|
||||
(unless (or isearch-suspended
|
||||
(eq eat--saved-line-input-history-isearch 'not-saved))
|
||||
(setq eat-line-input-history-isearch
|
||||
eat--saved-line-input-history-isearch)
|
||||
(setq eat--saved-line-input-history-isearch 'not-saved)))
|
||||
|
||||
(defun eat--line-goto-input (pos)
|
||||
"Put input history item of the absolute history position POS."
|
||||
;; If leaving the edit line, save partial unfinished input.
|
||||
(when (null eat--line-input-ring-index)
|
||||
(setq eat--line-stored-incomplete-input
|
||||
(buffer-substring-no-properties
|
||||
(eat-term-end eat--terminal) (point-max))))
|
||||
(setq eat--line-input-ring-index pos)
|
||||
(delete-region (eat-term-end eat--terminal) (point-max))
|
||||
(if (and pos (not (ring-empty-p eat--line-input-ring)))
|
||||
(insert (ring-ref eat--line-input-ring pos))
|
||||
;; Restore partial unfinished input.
|
||||
(when (> (length eat--line-stored-incomplete-input) 0)
|
||||
(insert eat--line-stored-incomplete-input))))
|
||||
|
||||
(defun eat--line-history-isearch-search ()
|
||||
"Return the proper search function, for Isearch in input history."
|
||||
(lambda (string bound noerror)
|
||||
(let ((search-fun (isearch-search-fun-default))
|
||||
found)
|
||||
;; Avoid lazy-highlighting matches in the input line and in the
|
||||
;; output when searching forward. Lazy-highlight calls this
|
||||
;; lambda with the bound arg, so skip the prompt and the output.
|
||||
(when (and bound isearch-forward
|
||||
(< (point) (eat-term-end eat--terminal)))
|
||||
(goto-char (eat-term-end eat--terminal)))
|
||||
(or
|
||||
;; 1. First try searching in the initial input line
|
||||
(funcall search-fun string (if isearch-forward
|
||||
bound
|
||||
(eat-term-end eat--terminal))
|
||||
noerror)
|
||||
;; 2. If the above search fails, start putting next/prev
|
||||
;; history elements in the input line successively, and search
|
||||
;; the string in them. Do this only when bound is nil
|
||||
;; (i.e. not while lazy-highlighting search strings in the
|
||||
;; current input line).
|
||||
(unless bound
|
||||
(condition-case nil
|
||||
(progn
|
||||
(while (not found)
|
||||
(cond
|
||||
(isearch-forward
|
||||
;; Signal an error here explicitly, because
|
||||
;; `eat-line-next-input' doesn't signal an
|
||||
;; error.
|
||||
(when (null eat--line-input-ring-index)
|
||||
(error "End of history; no next item"))
|
||||
(eat-line-next-input 1)
|
||||
(goto-char (eat-term-end eat--terminal)))
|
||||
(t
|
||||
;; Signal an error here explicitly, because
|
||||
;; `eat-line-previous-input' doesn't signal an
|
||||
;; error.
|
||||
(when (eq eat--line-input-ring-index
|
||||
(1- (ring-length eat--line-input-ring)))
|
||||
(error
|
||||
"Beginning of history; no preceding item"))
|
||||
(eat-line-previous-input 1)
|
||||
(goto-char (point-max))))
|
||||
(setq isearch-barrier (point))
|
||||
(setq isearch-opoint (point))
|
||||
;; After putting the next/prev history element,
|
||||
;; search the string in them again, until
|
||||
;; `eat-line-next-input' or `eat-line-previous-input'
|
||||
;; raises an error at the beginning/end of history.
|
||||
(setq found
|
||||
(funcall search-fun string
|
||||
(unless isearch-forward
|
||||
;; For backward search, don't search
|
||||
;; in the terminal region
|
||||
(eat-term-end eat--terminal))
|
||||
noerror)))
|
||||
;; Return point of the new search result
|
||||
(point))
|
||||
;; Return nil on the error "no next/preceding item"
|
||||
(error nil)))))))
|
||||
|
||||
(defun eat--line-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 shell prompt.
|
||||
Otherwise, it displays the standard Isearch message returned from
|
||||
the function `isearch-message'.
|
||||
|
||||
C-Q-HACK and ELLIPSIS are same as in function `isearch-message', which
|
||||
see."
|
||||
(if (not (and isearch-success (not isearch-error)))
|
||||
;; Use standard function `isearch-message' when not in input
|
||||
;; line, or search fails, or has an error (like incomplete
|
||||
;; regexp). This function displays isearch message in the echo
|
||||
;; area, so it's possible to see what is wrong in the search
|
||||
;; string.
|
||||
(isearch-message c-q-hack ellipsis)
|
||||
;; Otherwise, put the overlay with the standard Isearch prompt
|
||||
;; over the shell prompt.
|
||||
(if (overlayp eat--line-history-isearch-message-overlay)
|
||||
(move-overlay eat--line-history-isearch-message-overlay
|
||||
(save-excursion
|
||||
(goto-char (eat-term-end eat--terminal))
|
||||
(forward-line 0)
|
||||
(point))
|
||||
(eat-term-end eat--terminal))
|
||||
(setq eat--line-history-isearch-message-overlay
|
||||
(make-overlay (save-excursion
|
||||
(goto-char (eat-term-end eat--terminal))
|
||||
(forward-line 0)
|
||||
(point))
|
||||
(eat-term-end eat--terminal)))
|
||||
(overlay-put eat--line-history-isearch-message-overlay
|
||||
'evaporate t))
|
||||
(overlay-put eat--line-history-isearch-message-overlay
|
||||
'display (isearch-message-prefix
|
||||
ellipsis isearch-nonincremental))
|
||||
(if (and eat--line-input-ring-index (not ellipsis))
|
||||
;; Display the current history index.
|
||||
(message "History item: %d" (1+ eat--line-input-ring-index))
|
||||
;; Or clear a previous isearch message.
|
||||
(message ""))))
|
||||
|
||||
(defun eat--line-history-isearch-wrap ()
|
||||
"Wrap the input history search when search fails.
|
||||
|
||||
Move point to the first history element for a forward search,
|
||||
or to the last history element for a backward search."
|
||||
;; When `eat--line-history-isearch-search' fails on reaching the
|
||||
;; beginning/end of the history, wrap the search to the first/last
|
||||
;; input history element.
|
||||
(if isearch-forward
|
||||
(eat--line-goto-input (1- (ring-length eat--line-input-ring)))
|
||||
(eat--line-goto-input nil))
|
||||
(goto-char (if isearch-forward
|
||||
(eat-term-end eat--terminal)
|
||||
(point-max))))
|
||||
|
||||
(defun eat--line-history-isearch-push-state ()
|
||||
"Save a function restoring the state of input history search.
|
||||
|
||||
Save `eat--line-input-ring-index' to the additional state parameter
|
||||
in the search status stack."
|
||||
(let ((index eat--line-input-ring-index))
|
||||
(lambda (cmd)
|
||||
(eat--line-history-isearch-pop-state cmd index))))
|
||||
|
||||
(defun eat--line-history-isearch-pop-state (_cmd hist-pos)
|
||||
"Restore the input history search state.
|
||||
Go to the history element by the absolute history position HIST-POS."
|
||||
(eat--line-goto-input hist-pos))
|
||||
|
||||
|
||||
;;;;; Major Mode.
|
||||
|
||||
|
@ -6291,6 +6515,11 @@ END if it's safe to do so."
|
|||
eat--line-input-ring-index
|
||||
eat--line-stored-incomplete-input
|
||||
eat--line-matching-input-from-input-string
|
||||
eat--line-history-isearch-message-overlay
|
||||
isearch-search-fun-function
|
||||
isearch-message-function
|
||||
isearch-wrap-function
|
||||
isearch-push-state-function
|
||||
eat--pending-input-chunks
|
||||
eat--process-input-queue-timer
|
||||
eat--pending-output-chunks
|
||||
|
@ -6304,6 +6533,7 @@ END if it's safe to do so."
|
|||
#'eat--filter-buffer-substring)
|
||||
(setq bidi-paragraph-direction 'left-to-right)
|
||||
(setq eat--mouse-grabbing-type nil)
|
||||
(add-hook 'isearch-mode-hook 'eat--line-history-isearch-setup nil t)
|
||||
(setq mode-line-process
|
||||
'(""
|
||||
(:eval
|
||||
|
|
27
eat.texi
27
eat.texi
|
@ -423,12 +423,27 @@ the terminal.
|
|||
@kindex @key{TAB} @r{(``line mode'')}
|
||||
The input history is recorded. You can cycle through the history with
|
||||
@kbd{M-p} and @kbd{M-n}, and also with @kbd{C-@key{up}} and
|
||||
@kbd{C-@key{down}}. @kbd{M-r} searches the input history, using the
|
||||
minibuffer. @kbd{C-c M-r} and @kbd{C-c M-s} searches the input
|
||||
history taking the current input as the query. @kbd{C-c M-r} searches
|
||||
backward while @kbd{C-c M-s} searches forward. And @kbd{C-c C-r}
|
||||
searches the input history using minibuffer with completion, useful
|
||||
specially if you use any minibuffer completion UI/framework.
|
||||
@kbd{C-@key{down}}. @kbd{C-c M-r} and @kbd{C-c M-s} searches the
|
||||
input history taking the current input as the query. @kbd{C-c M-r}
|
||||
searches backward while @kbd{C-c M-s} searches forward. And @kbd{C-c
|
||||
C-r} searches the input history using minibuffer with completion,
|
||||
useful specially if you use any minibuffer completion UI/framework.
|
||||
|
||||
You can also use Isearch
|
||||
(@pxref{Incremental Search,,, emacs, GNU Emacs Manual}) to search
|
||||
through the input history, with @kbd{M-r}. You can also customize
|
||||
@code{eat-line-input-history-isearch} to use all standard Isearch
|
||||
commands to search the input history.
|
||||
|
||||
@vindex eat-line-input-history-isearch
|
||||
@defopt eat-line-input-history-isearch
|
||||
Controls where Isearch searches in Eat buffer. If @code{t}, usual
|
||||
Isearch commands in Eat buffer search in the input history. If
|
||||
@code{dwim}, Isearch keys search in the input history only when
|
||||
initial point position is on input line. When starting Isearch from
|
||||
other parts of the Eat buffer, they search in the Eat buffer. If
|
||||
@code{nil}, Isearch operates on the whole Eat buffer.
|
||||
@end defopt
|
||||
|
||||
Input history is not loaded from the shell history file, to do that,
|
||||
@xref{Line Mode Integration}.
|
||||
|
|
Loading…
Add table
Reference in a new issue