emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/eat baf0bb7e5f 1/3: Add prompt mode and process OSC 51 as


From: ELPA Syncer
Subject: [nongnu] elpa/eat baf0bb7e5f 1/3: Add prompt mode and process OSC 51 as UI Command
Date: Wed, 13 Sep 2023 12:59:32 -0400 (EDT)

branch: elpa/eat
commit baf0bb7e5f28b10eb94cf1e0634c295002ac92e9
Author: Akib Azmain Turja <akib@disroot.org>
Commit: Akib Azmain Turja <akib@disroot.org>

    Add prompt mode and process OSC 51 as UI Command
    
    * eat.el (eat-enable-native-shell-prompt-editing)
    (eat-prompt-input-ring-size)
    (eat-prompt-move-point-for-matching-input): New user option.
    * eat.el (eat--t-term): Remove slots 'cwd', 'prompt-start-fn',
    'prompt-end-fn', 'cont-prompt-start-fn', 'cont-prompt-end-fn',
    'cmd-start-fn' and 'cmd-finish-fn'.  Add new slot 'ui-cmd-fn'.
    * eat.el (eat--t-set-cwd): Support only the format used by OSC
    7.
    * eat.el (eat--t-prompt-start,  eat--t-prompt-end)
    (eat--t-cont-prompt-start,  eat--t-cont-prompt-end)
    (eat--t-set-cmd, eat--t-cmd-start, eat--t-cmd-finish): Remove
    function.
    * eat.el (eat--t-ui-cmd): New function.
    * eat.el (eat--t-handle-output): Remove all specialized OSC 51
    handlers and call 'eat--t-ui-cmd' to handle any OSC 51
    sequence.
    * eat.el (eat-term-set-parameter): Set 'ui-cmd-fn' slot of
    terminal when 'ui-command-function' is set.
    * eat.el (eat-term-cwd): Remove function.
    * eat.el (eat-term-prompt-start-function)
    (eat-term-prompt-end-function, eat-term-cmd-start-function)
    (eat-term-cont-prompt-start-function)
    (eat-term-cont-prompt-end-function, eat-term-set-cmd-function)
    (eat-term-cmd-finish-function): Remove generalized variables.
    * eat.el (eat-term-send-string): New function.
    * eat.el (eat-send-string-as-yank): Rename to
    'eat-term-send-string-as-yank'.
    All callers changed.
    * eat.el (eat--set-cwd-uic, eat--post-cont-prompt): New
    function.
    * eat.el (eat--pre-prompt, eat--post-prompt, eat--set-cmd)
    (eat--pre-cmd, eat--set-cmd-status): Remove unused first
    argument.
    All callers changed.
    * eat.el (eat--get-shell-history, eat--handle-uic): New
    function.
    * eat.el (eat-prompt-mode-map): New variable.
    * eat.el (eat-emacs-mode, eat-semi-char-mode, eat-char-mode):
    Handle the case when prompt mode is active.
    * eat.el (eat--prompt-mode-previous-mode): New variable.
    * eat.el (eat--prompt-mode): New non-interactive minor mode.
    * eat.el (eat-prompt-send-default, eat-prompt-send)
    (eat-prompt-newline, eat-prompt-delchar-or-eof)
    (eat-prompt-send-interrupt): New command.
    * eat.el (eat--prompt-input-ring, eat--prompt-input-ring-index)
    (eat--prompt-stored-incomplete-input)
    (eat--prompt-matching-input-from-input-string): New variable.
    * eat.el (eat--prompt-populate-input-ring)
    (eat--prompt-reset-input-ring-vars, eat--prompt-ask-for-regexp-arg)
    (eat--prompt-search-arg, eat--prompt-search-start)
    (eat--prompt-prev-input-string, eat--prompt-prev-matching-input-str)
    (eat--prompt-delete-input, eat--prompt-prev-matching-input-str-pos):
    New function.
    * eat.el (eat-prompt-previous-input, eat-prompt-next-input)
    (eat-prompt-restore-input, eat-prompt-previous-matching-input)
    (eat-prompt-next-matching-input, eat-prompt-find-input)
    (eat-prompt-previous-matching-input-from-input)
    (eat-prompt-next-matching-input-from-input): New command.
    * eat.el (eat-mode): Make 'eat--prompt-mode-previous-mode',
    'eat--prompt-input-ring', 'eat--prompt-input-ring-index',
    'eat--prompt-stored-incomplete-input' and
    'eat--prompt-matching-input-from-input-string' buffer-local.
    Record undo information if
    'eat-enable-native-shell-prompt-editing' is enabled.  Set
    'mode-line-process' properly so that prompt mode is shown when
    enabled.
    * eat.el (eat--process-output-queue): Remove any narrowing
    temporarily when called.  Don't
    * eat.el (eat--sentinel): Disable prompt mode.  Also fix the
    bug where the point centers when the program exits, instead of
    going to the end of buffer.
    * eat.el (eat-exec): Don't set removed generalized variables
    'eat-term-prompt-start-function', 'eat-term-set-cmd-function',
    'eat-term-prompt-end-function', 'eat-term-cmd-start-function'
    and 'eat-term-cmd-finish-function'.  Set terminal parameter
    'ui-command-function' to 'eat--handle-uic'.
    * eat.el (eat--eshell-handle-uic): New function.
    * eat.el (eat--eshell-output-filter): Let-bind
    'inhibit-read-only' only for required parts.
    * eat.el (eat--eshell-setup-proc-and-term): Set terminal
    parameter 'ui-command-function' to 'eat--eshell-handle-uic'.
    * eat.el (eat--eshell-sentinel): Fix the bug where the point
    centers when the program exits, instead of going to the end of
    buffer.
    * integration/bash (__eat_enable_integration): Send shell
    history.
---
 eat.el           | 971 ++++++++++++++++++++++++++++++++++++++-----------------
 integration/bash |  11 +
 2 files changed, 686 insertions(+), 296 deletions(-)

diff --git a/eat.el b/eat.el
index fae03b27e4..5d1fb32719 100644
--- a/eat.el
+++ b/eat.el
@@ -129,7 +129,7 @@ This is the default name used when running Eat."
 
 (defcustom eat-term-scrollback-size 131072 ; 128 K
   "Size of scrollback area in characters.  nil means unlimited."
-  :type '(choice integer (const nil))
+  :type '(choice natnum (const nil))
   :group 'eat-term
   :group 'eat-ui)
 
@@ -296,6 +296,44 @@ the history of commands like `eat', `shell-command' and
   :group 'eat-ui
   :group 'eat-eshell)
 
+(defcustom eat-enable-native-shell-prompt-editing t
+  "Non-nil means allowing editing shell prompt using Emacs commands.
+
+When non-nil, you can edit shell prompts with the normal Emacs editing
+commands."
+  :type 'boolean
+  :group 'eat-ui)
+
+(defcustom eat-prompt-input-ring-size 1000
+  "Number of input history items to keep."
+  :type 'natnump
+  :group 'eat-ui)
+
+(defcustom eat-prompt-move-point-for-matching-input 'after-input
+  "Controls where to place point after matching input.
+
+\\<eat-prompt-mode-map>This influences the commands \
+\\[eat-prompt-previous-matching-input-from-input] and \
+\\[eat-prompt-next-matching-input-from-input].
+If `after-input', point will be positioned after the input typed
+by the user, but before the rest of the history entry that has
+been inserted.  If `end-of-line', point will be positioned at the
+end of the current logical (not visual) line after insertion."
+  :type '(radio (const :tag "Stay after input" after-input)
+                (const :tag "Move to end of line" end-of-line))
+  :group 'eat-ui)
+
+(defcustom eat-prompt-input-send-function
+  #'eat-prompt-send-default
+  "Function to send the shell prompt input.
+
+The function is called without any argument.  The buffer is narrowed
+to the input.  The function may modify the input but mustn't modify
+the buffer restrictions.  It should call
+`eat-prompt-send-default' to send the final output."
+  :type 'function
+  :group 'eat-ui)
+
 (defcustom eat-enable-shell-prompt-annotation t
   "Non-nil means annotate shell prompt with the status of command.
 
@@ -991,9 +1029,6 @@ For example: when THRESHOLD is 3, \"*foobarbaz\" is 
converted to
   (begin nil :documentation "Beginning of terminal.")
   (end nil :documentation "End of terminal area.")
   (title "" :documentation "The title of the terminal.")
-  (cwd
-   (cons (system-name) default-directory)
-   :documentation "The working directory of the terminal.")
   (bell-fn
    (1value #'ignore)
    :documentation "Function to ring the bell.")
@@ -1018,27 +1053,9 @@ For example: when THRESHOLD is 3, \"*foobarbaz\" is 
converted to
   (set-cwd-fn
    (1value #'ignore)
    :documentation "Function to set the current working directory.")
-  (prompt-start-fn
-   (1value #'ignore)
-   :documentation "Function to call when prompt starts.")
-  (prompt-end-fn
-   (1value #'ignore)
-   :documentation "Function to call when prompt ends.")
-  (cont-prompt-start-fn
-   (1value #'ignore)
-   :documentation "Function to call when prompt starts.")
-  (cont-prompt-end-fn
-   (1value #'ignore)
-   :documentation "Function to call when prompt ends.")
-  (set-cmd-fn
+  (ui-cmd-fn
    (1value #'ignore)
-   :documentation "Function to set the command being executed.")
-  (cmd-start-fn
-   (1value #'ignore)
-   :documentation "Function to call just before a command is run.")
-  (cmd-finish-fn
-   (1value #'ignore)
-   :documentation "Function to call after a command has finished.")
+   :documentation "Function to handle UI command sequence.")
   (parser-state nil :documentation "State of parser.")
   (scroll-begin 1 :documentation "First line of scroll region.")
   (scroll-end 24 :documentation "Last line of scroll region.")
@@ -2680,42 +2697,17 @@ MODE should be one of nil and `x10', `normal', 
`button-event',
   ;; Inform the UI.
   (funcall (eat--t-term-set-title-fn eat--t-term) eat--t-term title))
 
-(defun eat--t-set-cwd (format host path)
-  "Set the working directory of terminal to URL.
-
-If FORMAT is `base64', HOST should be base64 encoded hostname and PATH
-should be base64 encoded working directory path.
-
-If FORMAT is `url', HOST should be nil and PATH should be an URL in
-the format \"file://HOST/CWD/\"; HOST can be empty."
-  (pcase-exhaustive format
-    ('base64
-     (let ((dir (ignore-errors (expand-file-name
-                                (file-name-as-directory
-                                 (decode-coding-string
-                                  (base64-decode-string path)
-                                  locale-coding-system)))))
-           (hostname (ignore-errors (decode-coding-string
-                                     (base64-decode-string host)
-                                     locale-coding-system))))
-       (when (and dir hostname)
-         ;; Update working directory.
-         (setf (eat--t-term-cwd eat--t-term) (cons hostname dir))
-         ;; Inform the UI.
-         (funcall (eat--t-term-set-cwd-fn eat--t-term)
-                  eat--t-term hostname dir))))
-    ('url
-     (let ((url (url-generic-parse-url path)))
-       (when (string= (url-type url) "file")
-         (let ((host (url-host url))
-               (dir (expand-file-name
-                     (file-name-as-directory
-                      (url-unhex-string (url-filename url))))))
-           ;; Update working directory.
-           (setf (eat--t-term-cwd eat--t-term) (cons host dir))
-           ;; Inform the UI.
-           (funcall (eat--t-term-set-cwd-fn eat--t-term)
-                    eat--t-term host dir)))))))
+(defun eat--t-set-cwd (url)
+  "Set the working directory of terminal to URL."
+  (setq url (url-generic-parse-url url))
+  (when (string= (url-type url) "file")
+    (let ((host (url-host url))
+          (dir (expand-file-name
+                (file-name-as-directory
+                 (url-unhex-string (url-filename url))))))
+      ;; Inform the UI.
+      (funcall (eat--t-term-set-cwd-fn eat--t-term)
+               eat--t-term host dir))))
 
 (defun eat--t-send-device-attrs (n format)
   "Return device attributes.
@@ -3119,38 +3111,9 @@ If NULLIFY is non-nil, nullify flushed part of Sixel 
buffer."
   "Disable Sixel scrolling mode."
   (setf (eat--t-term-sixel-scroll-mode eat--t-term) nil))
 
-(defun eat--t-prompt-start ()
-  "Call shell prompt start hook."
-  (funcall (eat--t-term-prompt-start-fn eat--t-term) eat--t-term))
-
-(defun eat--t-prompt-end ()
-  "Call shell prompt end hook."
-  (funcall (eat--t-term-prompt-end-fn eat--t-term) eat--t-term))
-
-(defun eat--t-cont-prompt-start ()
-  "Call shell prompt start hook."
-  (funcall (eat--t-term-cont-prompt-start-fn eat--t-term)
-           eat--t-term))
-
-(defun eat--t-cont-prompt-end ()
-  "Call shell prompt end hook."
-  (funcall (eat--t-term-cont-prompt-end-fn eat--t-term) eat--t-term))
-
-(defun eat--t-set-cmd (cmd)
-  "Set the command being executed to CMD."
-  (when-let* ((c (ignore-errors (decode-coding-string
-                                 (base64-decode-string cmd)
-                                 locale-coding-system))))
-    (funcall (eat--t-term-set-cmd-fn eat--t-term) eat--t-term c)))
-
-(defun eat--t-cmd-start ()
-  "Call shell command start hook."
-  (funcall (eat--t-term-cmd-start-fn eat--t-term) eat--t-term))
-
-(defun eat--t-cmd-finish (status)
-  "Call shell command finish hook with exit status STATUS."
-  (funcall (eat--t-term-cmd-finish-fn eat--t-term) eat--t-term
-           status))
+(defun eat--t-ui-cmd (cmd)
+  "Call UI's UIC handler to handle CMD."
+  (funcall (eat--t-term-ui-cmd-fn eat--t-term) eat--t-term cmd))
 
 (defun eat--t-set-modes (params format)
   "Set modes according to PARAMS in format FORMAT."
@@ -3595,52 +3558,18 @@ If NULLIFY is non-nil, nullify flushed part of Sixel 
buffer."
                       ((rx string-start ?7 ?\;
                            (let url (zero-or-more anything))
                            string-end)
-                       (eat--t-set-cwd 'url nil url))
+                       (eat--t-set-cwd url))
                       ;; OSC 1 0 ; ? ST.
                       ("10;?"
                        (eat--t-report-foreground-color))
                       ;; OSC 1 1 ; ? ST.
                       ("11;?"
                        (eat--t-report-background-color))
-                      ;; In XTerm, OSC 51 is reserved for Emacs shell.
-                      ;; I have no idea why, but Vterm uses this OSC
-                      ;; to set the current directory and remotely
-                      ;; execute Emacs Lisp code.  Vterm uses the
-                      ;; characters 'A' and 'E' as the first character
-                      ;; of second parameter of this OSC.  We use 'e'
-                      ;; as the second parameter, followed by one or
-                      ;; more parameters.
-                      ;; OSC 5 1 ; e ; A ; <t> ; <s> ST
-                      ((rx string-start "51;e;A;"
-                           (let host (zero-or-more (not ?\;)))
-                           ?\; (let path (zero-or-more anything))
-                           string-end)
-                       (eat--t-set-cwd 'base64 host path))
-                      ;; OSC 5 1 ; e ; B ST
-                      ("51;e;B"
-                       (eat--t-prompt-start))
-                      ;; OSC 5 1 ; e ; C ST
-                      ("51;e;C"
-                       (eat--t-prompt-end))
-                      ;; OSC 5 1 ; e ; D ST
-                      ("51;e;D"
-                       (eat--t-cont-prompt-start))
-                      ;; OSC 5 1 ; e ; E ST
-                      ("51;e;E"
-                       (eat--t-cont-prompt-end))
-                      ;; OSC 5 1 ; e ; F ; <t> ST
-                      ((rx string-start "51;e;F;"
+                      ;; OSC 5 1 ; <s> ST.
+                      ((rx string-start "51;"
                            (let cmd (zero-or-more anything))
                            string-end)
-                       (eat--t-set-cmd cmd))
-                      ;; OSC 5 1 ; e ; G ST
-                      ("51;e;G"
-                       (eat--t-cmd-start))
-                      ;; OSC 5 1 ; e ; H ; <n> ST
-                      ((rx string-start "51;e;H;"
-                           (let status (one-or-more digit))
-                           string-end)
-                       (eat--t-cmd-finish (string-to-number status)))
+                       (eat--t-ui-cmd cmd))
                       ;; OSC 5 2 ; <t> ; <s> ST.
                       ((rx string-start "52;"
                            (let targets
@@ -4009,6 +3938,10 @@ If NULLIFY is non-nil, nullify flushed part of Sixel 
buffer."
        (signal 'wrong-type-argument (list '(symbolp facep) value)))
      (setf (aref (eat--t-term-font-faces terminal) index)
            value))
+    ('ui-command-function
+     (unless (functionp value)
+       (signal 'wrong-type-argument (list 'functionp value)))
+     (setf (eat--t-term-ui-cmd-fn terminal) value))
     ('char-dimensions
      (unless (and (consp value)
                   (integerp (car value))
@@ -4185,13 +4118,6 @@ FUNCTION), where FUNCTION is the function to set title."
 (gv-define-setter eat-term-set-title-function (function terminal)
   `(setf (eat--t-term-set-title-fn ,terminal) ,function))
 
-(defun eat-term-cwd (terminal)
-  "Return the current working directory of TERMINAL with the hostname.
-
-The return value is of form (HOST . PATH), where HOST is the hostname
-and PATH is the working directory."
-  (eat--t-term-cwd terminal))
-
 (defun eat-term-set-cwd-function (terminal)
   "Return the function used to set the working directory of TERMINAL.
 
@@ -4210,104 +4136,6 @@ directory."
 (gv-define-setter eat-term-set-cwd-function (function terminal)
   `(setf (eat--t-term-set-cwd-fn ,terminal) ,function))
 
-(defun eat-term-prompt-start-function (terminal)
-  "Return the function called just before shell prompt.
-
-The function is called with with a single argument, TERMINAL.  The
-function should not change point and buffer restriction.
-
-To set it, use (`setf' (`eat-term-prompt-start-function' TERMINAL)
-FUNCTION), where FUNCTION is the function to call."
-  (eat--t-term-prompt-start-fn terminal))
-
-(gv-define-setter eat-term-prompt-start-function (function terminal)
-  `(setf (eat--t-term-prompt-start-fn ,terminal) ,function))
-
-(defun eat-term-prompt-end-function (terminal)
-  "Return the function called just after shell prompt.
-
-The function is called with with a single argument, TERMINAL.  The
-function should not change point and buffer restriction.
-
-To set it, use (`setf' (`eat-term-prompt-end-function' TERMINAL)
-FUNCTION), where FUNCTION is the function to call."
-  (eat--t-term-prompt-end-fn terminal))
-
-(gv-define-setter eat-term-prompt-end-function (function terminal)
-  `(setf (eat--t-term-prompt-end-fn ,terminal) ,function))
-
-(defun eat-term-continuation-prompt-start-function (terminal)
-  "Return the function called just before shell continuation prompt.
-
-The function is called with with a single argument, TERMINAL.  The
-function should not change point and buffer restriction.
-
-To set it, use (`setf' (`eat-term-continuation-prompt-start-function'
-TERMINAL) FUNCTION), where FUNCTION is the function to call."
-  (eat--t-term-cont-prompt-start-fn terminal))
-
-(gv-define-setter eat-term-continuation-prompt-start-function
-    (function terminal)
-  `(setf (eat--t-term-cont-prompt-start-fn ,terminal) ,function))
-
-(defun eat-term-continuation-prompt-end-function (terminal)
-  "Return the function called just after shell continuation prompt.
-
-The function is called with with a single argument, TERMINAL.  The
-function should not change point and buffer restriction.
-
-To set it, use (`setf' (`eat-term-continuation-prompt-end-function'
-TERMINAL) FUNCTION), where FUNCTION is the function to call."
-  (eat--t-term-cont-prompt-end-fn terminal))
-
-(gv-define-setter eat-term-continuation-prompt-end-function
-    (function terminal)
-  `(setf (eat--t-term-cont-prompt-end-fn ,terminal) ,function))
-
-(defun eat-term-set-cmd-function (terminal)
-  "Return the function used to set the command being run in TERMINAL.
-
-The function is called with two arguments, TERMINAL, the host where
-the directory is, and the new (current) working directory of TERMINAL.
-The function should not change point and buffer restriction.
-
-Note that the client is responsible for the arguments to the function,
-verify them before using.
-
-To set it, use (`setf' (`eat-term-set-cwd-function' TERMINAL)
-FUNCTION), where FUNCTION is the function to set the command."
-  (eat--t-term-set-cmd-fn terminal))
-
-(gv-define-setter eat-term-set-cmd-function (function terminal)
-  `(setf (eat--t-term-set-cmd-fn ,terminal) ,function))
-
-(defun eat-term-cmd-start-function (terminal)
-  "Return the function called just before a command is run in shell.
-
-The function is called with with a single argument, TERMINAL.  The
-function should not change point and buffer restriction.
-
-To set it, use (`setf' (`eat-term-cmd-start-function' TERMINAL)
-FUNCTION), where FUNCTION is the function to call."
-  (eat--t-term-cmd-start-fn terminal))
-
-(gv-define-setter eat-term-cmd-start-function (function terminal)
-  `(setf (eat--t-term-cmd-start-fn ,terminal) ,function))
-
-(defun eat-term-cmd-finish-function (terminal)
-  "Return the function called after a command has finished in shell.
-
-The function is called with with a two arguments, TERMINAL and the
-exit status of the command.  The function should not change point and
-buffer restriction.
-
-To set it, use (`setf' (`eat-term-cmd-finish-function' TERMINAL)
-FUNCTION), where FUNCTION is the function to call."
-  (eat--t-term-cmd-finish-fn terminal))
-
-(gv-define-setter eat-term-cmd-finish-function (function terminal)
-  `(setf (eat--t-term-cmd-finish-fn ,terminal) ,function))
-
 (defun eat-term-size (terminal)
   "Return the size of TERMINAL as (WIDTH . HEIGHT)."
   (let ((disp (eat--t-term-display terminal)))
@@ -4727,7 +4555,11 @@ client process may get confused."
            (when (eat--t-term-focus-event-mode terminal)
              (send "\e[O"))))))))
 
-(defun eat-send-string-as-yank (terminal args)
+(defun eat-term-send-string (terminal string)
+  "Send STRING to TERMINAL directly."
+  (funcall (eat--t-term-input-fn terminal) terminal string))
+
+(defun eat-term-send-string-as-yank (terminal args)
   "Send ARGS to TERMINAL, honoring bracketed yank mode.
 
 Each argument in ARGS can be either string or character."
@@ -5184,7 +5016,20 @@ If HOST isn't the host Emacs is running on, don't do 
anything."
     (ignore-errors
       (cd-absolute cwd))))
 
-(defun eat--pre-prompt (_)
+(defun eat--set-cwd-uic (host path)
+  "Set PATH to the CWD, if HOST is same as the host name."
+  (let ((dir (ignore-errors (expand-file-name
+                             (file-name-as-directory
+                              (decode-coding-string
+                               (base64-decode-string path)
+                               locale-coding-system)))))
+        (hostname (ignore-errors (decode-coding-string
+                                  (base64-decode-string host)
+                                  locale-coding-system))))
+    (when (and dir hostname)
+      (eat--set-cwd nil hostname dir))))
+
+(defun eat--pre-prompt ()
   "Save the beginning position of shell prompt."
   (setq eat--shell-prompt-begin (point-marker))
   ;; FIXME: It's a crime to touch processes in this section.
@@ -5192,8 +5037,8 @@ If HOST isn't the host Emacs is running on, don't do 
anything."
     (set-process-query-on-exit-flag
      (eat-term-parameter eat--terminal 'eat--process) nil)))
 
-(defun eat--post-prompt (_)
-  "Put a mark in the marginal area on current line."
+(defun eat--post-prompt ()
+  "Put a mark in the marginal area and enter prompt mode."
   (when eat-enable-shell-prompt-annotation
     (let ((indicator
            (if (zerop eat--shell-command-status)
@@ -5237,7 +5082,14 @@ If HOST isn't the host Emacs is running on, don't do 
anything."
       ;; Put a text property to allow shell prompt navigation.
       (put-text-property (1- (point)) (point)
                          'eat--shell-prompt-end t)))
-  (setq eat--shell-prompt-begin nil))
+  (setq eat--shell-prompt-begin nil)
+  (when eat-enable-native-shell-prompt-editing
+    (eat--prompt-mode +1)))
+
+(defun eat--post-cont-prompt ()
+  "Enter prompt mode."
+  (when eat-enable-native-shell-prompt-editing
+    (eat--prompt-mode +1)))
 
 (defun eat--correct-shell-prompt-mark-overlays (buffer)
   "Correct all overlays used to add mark before shell prompt.
@@ -5295,12 +5147,15 @@ BUFFER is the terminal buffer."
                            (min (eat-term-end eat--terminal)
                                 (point-max))))))))))
 
-(defun eat--set-cmd (_ cmd)
+(defun eat--set-cmd (cmd)
   "Add CMD to `shell-command-history'."
-  (when eat-enable-shell-command-history
-    (add-to-history 'shell-command-history cmd)))
+  (when-let* (((and eat-enable-shell-command-history))
+              (command (ignore-errors (decode-coding-string
+                                       (base64-decode-string cmd)
+                                       locale-coding-system))))
+    (add-to-history 'shell-command-history command)))
 
-(defun eat--pre-cmd (_)
+(defun eat--pre-cmd ()
   "Update shell prompt mark to indicate command is running."
   ;; FIXME: It's a crime to touch processes in this section.
   (when (eq eat-query-before-killing-running-terminal 'auto)
@@ -5313,12 +5168,96 @@ BUFFER is the terminal buffer."
            eat-shell-prompt-annotation-running-margin-indicator
            'face '(eat-shell-prompt-annotation-running default)))))
 
-(defun eat--set-cmd-status (_ code)
+(defun eat--set-cmd-status (code)
   "Set CODE as the current shell command's exit status."
   (when eat-enable-shell-prompt-annotation
     ;; We'll update the mark later when the prompt appears.
     (setq eat--shell-command-status code)))
 
+(defun eat--get-shell-history (hist)
+  "Get shell history from HIST."
+  (pcase hist
+    (`(,host . ,file)
+     (setq host (ignore-errors
+                  (decode-coding-string (base64-decode-string host)
+                                        locale-coding-system)))
+     (setq file (ignore-errors
+                  (decode-coding-string (base64-decode-string file)
+                                        locale-coding-system)))
+     (if (not eat-enable-native-shell-prompt-editing)
+         (eat-term-send-string eat--terminal "\e]51;e;I;0\e\\")
+       (if (and host file
+                (string= host (system-name))
+                (file-readable-p file))
+           (let ((str nil))
+             (eat-term-send-string eat--terminal "\e]51;e;I;0\e\\")
+             (with-temp-buffer
+               (insert-file-contents file)
+               (setq str (buffer-string)))
+             (eat--prompt-populate-input-ring str))
+         (eat-term-send-string
+          eat--terminal
+          (format "\e]51;e;I;%s\e\\"
+                  eat-prompt-input-ring-size)))))
+    ((pred stringp)
+     (eat--prompt-populate-input-ring
+      (ignore-errors
+        (decode-coding-string (base64-decode-string hist)
+                              locale-coding-system))))))
+
+(defun eat--handle-uic (_ cmd)
+  "Handle UI Command sequence CMD."
+  (pcase cmd
+    ;; In XTerm, OSC 51 is reserved for Emacs shell.  I have no idea
+    ;; why, but Vterm uses this OSC to set the current directory and
+    ;; remotely execute Emacs Lisp code.  Vterm uses the characters
+    ;; 'A' and 'E' as the first character of second parameter of this
+    ;; OSC.  We use 'e' as the second parameter, followed by one or
+    ;; more parameters.
+    ;; UIC e ; A ; <t> ; <s> ST.
+    ((rx string-start "e;A;"
+         (let host (zero-or-more (not ?\;)))
+         ?\; (let path (zero-or-more anything))
+         string-end)
+     (eat--set-cwd-uic host path))
+    ;; UIC e ; B ST.
+    ("e;B"
+     (eat--pre-prompt))
+    ;; UIC e ; C ST.
+    ("e;C"
+     (eat--post-prompt))
+    ;; UIC e ; D ST.
+    ("e;D"
+     ;; Start of continuation prompt.
+     ;; Defined but unused.
+     )
+    ;; UIC e ; E ST.
+    ("e;E"
+     (eat--post-cont-prompt))
+    ;; UIC e ; F ; <t> ST.
+    ((rx string-start "e;F;"
+         (let cmd (zero-or-more anything))
+         string-end)
+     (eat--set-cmd cmd))
+    ;; UIC e ; G ST
+    ("e;G"
+     (eat--pre-cmd))
+    ;; UIC e ; H ; <n> ST.
+    ((rx string-start "e;H;"
+         (let status (one-or-more digit))
+         string-end)
+     (eat--set-cmd-status (string-to-number status)))
+    ;; UIC e ; I ; <n> ST.
+    ((rx string-start "e;I;0;"
+         (let host (zero-or-more (not ?\;)))
+         ?\; (let path (zero-or-more anything))
+         string-end)
+     (eat--get-shell-history (cons host path)))
+    ((rx string-start "e;I;1;"
+         (let hist (zero-or-more anything))
+         string-end)
+     (eat--get-shell-history hist))))
+
 (defun eat-previous-shell-prompt (&optional arg)
   "Go to the previous shell prompt.
 
@@ -5531,7 +5470,7 @@ ARG is passed to `yank', which see."
   (when eat--terminal
     (funcall eat--synchronize-scroll-function
              (eat--synchronize-scroll-windows 'force-selected))
-    (eat-send-string-as-yank
+    (eat-term-send-string-as-yank
      eat--terminal
      (let ((yank-hook (bound-and-true-p yank-transform-functions)))
        (with-temp-buffer
@@ -5548,7 +5487,7 @@ STRING and ARG are passed to `yank-pop', which see."
   (when eat--terminal
     (funcall eat--synchronize-scroll-function
              (eat--synchronize-scroll-windows 'force-selected))
-    (eat-send-string-as-yank
+    (eat-term-send-string-as-yank
      eat--terminal
      (let ((yank-hook (bound-and-true-p yank-transform-functions)))
        (with-temp-buffer
@@ -5567,7 +5506,8 @@ EVENT is the mouse event."
   (unless (windowp (posn-window (event-start event)))
     (error "Position not in text area of window"))
   (select-window (posn-window (event-start event)))
-  (eat-send-string-as-yank eat--terminal (gui-get-primary-selection)))
+  (eat-term-send-string-as-yank
+   eat--terminal (gui-get-primary-selection)))
 
 (defun eat-mouse-yank-secondary (&optional event)
   "Send the secondary selection to the terminal.
@@ -5579,7 +5519,7 @@ EVENT is the mouse event."
   (select-window (posn-window (event-start event)))
   (let ((secondary (gui-get-selection 'SECONDARY)))
     (if secondary
-        (eat-send-string-as-yank eat--terminal secondary)
+        (eat-term-send-string-as-yank eat--terminal secondary)
       (error "No secondary selection"))))
 
 (defun eat-xterm-paste (event)
@@ -5594,7 +5534,7 @@ EVENT is the mouse event."
         (let ((interprogram-paste-function (lambda () pasted-text)))
           (eat-yank))
       ;; Insert the text without putting it onto the kill ring.
-      (eat-send-string-as-yank eat--terminal pasted-text))))
+      (eat-term-send-string-as-yank eat--terminal pasted-text))))
 
 ;; When changing these keymaps, be sure to update the manual, README
 ;; and commentary.
@@ -5647,6 +5587,27 @@ EVENT is the mouse event."
     map)
   "Keymap for Eat char mode.")
 
+(defvar eat-prompt-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [?\C-c ?\C-e] #'eat-emacs-mode)
+    (define-key map [?\t] #'completion-at-point)
+    (define-key map [?\C-m] #'eat-prompt-newline)
+    (define-key map [?\C-d] #'eat-prompt-delchar-or-eof)
+    (define-key map [?\C-c ?\C-c] #'eat-prompt-send-interrupt)
+    (define-key map [?\C-c ?\s] #'newline)
+    (define-key map [?\M-p] #'eat-prompt-previous-input)
+    (define-key map [?\M-n] #'eat-prompt-next-input)
+    (define-key map [C-up] #'eat-prompt-previous-input)
+    (define-key map [C-down] #'eat-prompt-next-input)
+    (define-key map [?\M-r] #'eat-prompt-previous-matching-input)
+    (define-key map [?\C-c ?\M-r] #'eat-prompt-find-input)
+    (define-key map [?\C-c ?\M-r]
+                #'eat-prompt-previous-matching-input-from-input)
+    (define-key map [?\C-c ?\M-s]
+                #'eat-prompt-next-matching-input-from-input)
+    map)
+  "Keymap for Eat native shell prompt mode.")
+
 (defvar eat--mouse-click-mode-map
   (eat-term-make-keymap #'eat-self-input '(:mouse-click) nil)
   "Keymap for `eat--mouse-click-mode'.")
@@ -5681,9 +5642,18 @@ EVENT is the mouse event."
   "Minor mode for mouse movement keymap."
   :interactive nil)
 
+(defvar eat--prompt-mode)
+(defvar eat--prompt-mode-previous-mode)
+
 (defun eat-emacs-mode ()
   "Switch to Emacs keybindings mode."
   (interactive)
+  (when (and eat--prompt-mode
+             (/= (eat-term-end eat--terminal) (point-max)))
+    (user-error "Can't switch to Emacs mode from prompt mode when\
+ input is non-empty"))
+  (setq eat--prompt-mode-previous-mode 'dont-restore)
+  (eat--prompt-mode -1)
   (eat--semi-char-mode -1)
   (eat--char-mode -1)
   (setq buffer-read-only t)
@@ -5695,7 +5665,13 @@ EVENT is the mouse event."
   (interactive)
   (unless eat--terminal
     (error "Process not running"))
+  (when (and eat--prompt-mode
+             (/= (eat-term-end eat--terminal) (point-max)))
+    (user-error "Can't switch to semi-char mode from prompt mode when\
+ input is non-empty"))
   (setq buffer-read-only nil)
+  (setq eat--prompt-mode-previous-mode 'dont-restore)
+  (eat--prompt-mode -1)
   (eat--char-mode -1)
   (eat--semi-char-mode +1)
   (eat--grab-mouse nil eat--mouse-grabbing-type)
@@ -5706,7 +5682,13 @@ EVENT is the mouse event."
   (interactive)
   (unless eat--terminal
     (error "Process not running"))
+  (when (and eat--prompt-mode
+             (/= (eat-term-end eat--terminal) (point-max)))
+    (user-error "Can't switch to char mode from prompt mode when\
+ input is non-empty"))
   (setq buffer-read-only nil)
+  (setq eat--prompt-mode-previous-mode 'dont-restore)
+  (eat--prompt-mode -1)
   (eat--semi-char-mode -1)
   (eat--char-mode +1)
   (eat--grab-mouse nil eat--mouse-grabbing-type)
@@ -5758,6 +5740,346 @@ MODE should one of:
      (eat--mouse-movement-mode -1))))
 
 
+;;;;; Prompt Mode.
+
+(defvar eat--prompt-mode-previous-mode nil
+  "The input mode active before prompt mode.")
+
+(define-minor-mode eat--prompt-mode
+  "Minor mode for prompt mode."
+  :interactive nil
+  :keymap eat-prompt-mode-map
+  (if eat--prompt-mode
+      (unless eat--prompt-mode-previous-mode
+        (setq eat--prompt-mode-previous-mode
+              (cond (eat--semi-char-mode 'semi-char)
+                    (eat--char-mode 'char)
+                    (t 'emacs)))
+        (eat--semi-char-mode -1)
+        (eat--char-mode -1)
+        (eat--grab-mouse nil eat--mouse-grabbing-type)
+        (setq buffer-read-only nil)
+        (eat--prompt-reset-input-ring-vars))
+    (when eat--prompt-mode-previous-mode
+      (pcase eat--prompt-mode-previous-mode
+        ('emacs (eat-emacs-mode))
+        ('semi-char (eat-semi-char-mode))
+        ('char (eat-char-mode)))
+      (setq eat--prompt-mode-previous-mode nil)
+      ;; Delete the undo list so that `undo' doesn't mess up with the
+      ;; terminal.
+      (setq buffer-undo-list nil))))
+
+(defun eat-prompt-send-default ()
+  "Send shell prompt input directly to the terminal."
+  (eat-term-send-string eat--terminal (buffer-string))
+  ;; If output arrives after sending the string, new output may get
+  ;; included in the narrowed region.  So we narrow it again so that
+  ;; we don't get a `text-read-only' for trying to delete text in the
+  ;; terminal.
+  (narrow-to-region (eat-term-end eat--terminal) (point-max)))
+
+(defun eat-prompt-send ()
+  "Send shell prompt input to the terminal."
+  (interactive)
+  (save-excursion
+    (save-restriction
+      (narrow-to-region (eat-term-end eat--terminal) (point-max))
+      (funcall eat-prompt-input-send-function)
+      (delete-region (point-min) (point-max))
+      (eat--prompt-mode -1)))
+  (goto-char (eat-term-display-cursor eat--terminal)))
+
+(defvar eat--prompt-input-ring)
+
+(defun eat-prompt-newline ()
+  "Send shell prompt input to the terminal, with a newline."
+  (interactive)
+  (if (not (<= (eat-term-end eat--terminal) (point)))
+      (call-interactively #'newline)
+    (unless (= (eat-term-end eat--terminal) (point-max))
+      (ring-insert eat--prompt-input-ring
+                   (buffer-substring-no-properties
+                    (eat-term-end eat--terminal) (point-max))))
+    (goto-char (point-max))
+    (insert "\n")
+    (eat-prompt-send)))
+
+(defun eat-prompt-delchar-or-eof (arg)
+  "Delete character or send shell prompt input to the terminal.
+
+ARG is the prefix arg, passed to `delete-char' when deleting
+character."
+  (interactive "p")
+  (if (not (= (eat-term-end eat--terminal) (point-max)))
+      (delete-char arg)
+    (insert "\C-d")
+    (eat-prompt-send)))
+
+(defun eat-prompt-send-interrupt ()
+  "Clear the input and send `C-c' to the shell."
+  (interactive)
+  (delete-region (eat-term-end eat--terminal) (point-max))
+  (goto-char (point-max))
+  (insert "\C-c")
+  (eat-prompt-send))
+
+
+;;;;;; History.
+
+;; The following code in this page (or section) is adapted from
+;; Comint source.
+
+(defvar eat--prompt-input-ring nil
+  "Ring holding the history of inputs.")
+
+(defvar eat--prompt-input-ring-index nil
+  "Index of last matched history element.")
+
+(defvar eat--prompt-stored-incomplete-input nil
+  "Stored input for history cycling.")
+
+(defvar eat--prompt-matching-input-from-input-string ""
+  "Input previously used to match input history.")
+
+(defun eat--prompt-reset-input-ring-vars ()
+  "Reset variable after a new shell prompt."
+  (setq eat--prompt-input-ring-index nil)
+  (setq eat--prompt-stored-incomplete-input nil)
+  (setq eat--prompt-matching-input-from-input-string ""))
+
+(defun eat--prompt-populate-input-ring (hist)
+  "Populate `eat--prompt-input-ring' from HIST."
+  (setq eat--prompt-input-ring (make-ring eat-prompt-input-ring-size))
+  (dolist (item (string-split hist "\n" 'omit-nulls))
+    (ring-insert eat--prompt-input-ring item)))
+
+(defun eat--prompt-ask-for-regexp-arg (prompt)
+  "Return list of regexp and prefix arg using PROMPT."
+  (let* (;; Don't clobber this.
+        (last-command last-command)
+        (regexp (read-from-minibuffer
+                  prompt nil nil nil
+                 'minibuffer-history-search-history)))
+    ;; If the user didn't enter anything, nothing is added to m-h-s-h.
+    ;; Use the previous search regexp, if there is one.
+    (list (if (string-equal regexp "")
+              (or (car minibuffer-history-search-history)
+                  regexp)
+           regexp)
+         (prefix-numeric-value current-prefix-arg))))
+
+(defun eat--prompt-search-arg (arg)
+  "Check point, and return ARG, or one if ARG is zero."
+  ;; First make sure there is a ring and that we are after the
+  ;; terminal region.
+  (cond ((< (point) (eat-term-end eat--terminal))
+        (user-error "Not at command line"))
+       ((or (null eat--prompt-input-ring)
+            (ring-empty-p eat--prompt-input-ring))
+        (user-error "Empty input ring"))
+       ((zerop arg)
+        ;; ARG zero resets search from beginning, and uses ARG 1.
+        (setq eat--prompt-input-ring-index nil)
+        1)
+       (t
+        arg)))
+
+(defun eat-prompt-restore-input ()
+  "Restore unfinished input."
+  (interactive)
+  (when eat--prompt-input-ring-index
+    (eat--prompt-delete-input)
+    (when (> (length eat--prompt-stored-incomplete-input) 0)
+      (insert eat--prompt-stored-incomplete-input)
+      (message "Input restored"))
+    (setq eat--prompt-input-ring-index nil)))
+
+(defun eat--prompt-search-start (arg)
+  "Index to start a directional search, ARG indicates the direction."
+  (if eat--prompt-input-ring-index
+      ;; If a search is running, offset by 1 in direction of ARG.
+      (mod (+ eat--prompt-input-ring-index (if (> arg 0) 1 -1))
+          (ring-length eat--prompt-input-ring))
+    ;; For a new search, start from end if ARG is negative, or from
+    ;; beginning otherwise.
+    (if (> arg 0)
+       0
+      (1- (ring-length eat--prompt-input-ring)))))
+
+(defun eat--prompt-prev-input-string (arg)
+  "Return the string ARG places along the input ring.
+Moves relative to `eat--prompt-input-ring-index'."
+  (ring-ref eat--prompt-input-ring
+            (if eat--prompt-input-ring-index
+               (mod (+ arg eat--prompt-input-ring-index)
+                    (ring-length eat--prompt-input-ring))
+             arg)))
+
+(defun eat-prompt-previous-input (arg)
+  "Cycle backwards through input history, saving input.
+
+Negative ARG means search forward instead."
+  (interactive "*p")
+  (if (and eat--prompt-input-ring-index
+           ;; Are we leaving the "end" of the ring?
+          (or (and (< arg 0)           ; going down
+                   (eq eat--prompt-input-ring-index 0))
+              (and (> arg 0)           ; going up
+                   (eq eat--prompt-input-ring-index
+                       (1- (ring-length eat--prompt-input-ring)))))
+          eat--prompt-stored-incomplete-input)
+      (eat-prompt-restore-input)
+    (eat-prompt-previous-matching-input "." arg)))
+
+(defun eat-prompt-next-input (arg)
+  "Cycle forwards through input history, saving input.
+
+Negative ARG means search backward instead."
+  (interactive "*p")
+  (eat-prompt-previous-input (- arg)))
+
+(defun eat--prompt-prev-matching-input-str (regexp arg)
+  "Return the string matching REGEXP ARG places along the input ring.
+Moves relative to `eat--prompt-input-ring-index'."
+  (let* ((pos (eat--prompt-prev-matching-input-str-pos regexp arg)))
+    (if pos (ring-ref eat--prompt-input-ring pos))))
+
+(defun eat--prompt-prev-matching-input-str-pos
+    (regexp arg &optional start)
+  "Return the index matching REGEXP ARG places along the input ring.
+Moves relative to START, or `eat--prompt-input-ring-index'."
+  (when (or (not (ring-p eat--prompt-input-ring))
+           (ring-empty-p eat--prompt-input-ring))
+    (user-error "No history"))
+  (let* ((len (ring-length eat--prompt-input-ring))
+        (motion (if (> arg 0) 1 -1))
+        (n (mod (- (or start (eat--prompt-search-start arg)) motion)
+                 len))
+        (tried-each-ring-item nil)
+        (prev nil))
+    ;; Do the whole search as many times as the argument says.
+    (while (and (/= arg 0) (not tried-each-ring-item))
+      ;; Step once.
+      (setq prev n)
+      (setq n (mod (+ n motion) len))
+      ;; If we haven't reached a match, step some more.
+      (while (and (< n len) (not tried-each-ring-item)
+                 (not (string-match regexp
+                                     (ring-ref
+                                      eat--prompt-input-ring n))))
+       (setq n (mod (+ n motion) len))
+       ;; If we have gone all the way around in this search.
+       (setq tried-each-ring-item (= n prev)))
+      (setq arg (if (> arg 0) (1- arg) (1+ arg))))
+    ;; Now that we know which ring element to use, if we found it,
+    ;; return that.
+    (when (string-match regexp (ring-ref eat--prompt-input-ring n))
+      n)))
+
+(defun eat--prompt-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-prompt-previous-matching-input (regexp n &optional restore)
+  "Search backwards through input history for match for REGEXP.
+
+\(Previous history elements are earlier commands.)
+With prefix argument N, search for Nth previous match.
+If N is negative, find the next or Nth next match.
+
+If RESTORE is non-nil, restore input in case of wrap."
+  (interactive (eat--prompt-ask-for-regexp-arg
+                "Previous input matching (regexp): "))
+  (setq n (eat--prompt-search-arg n))
+  (let ((pos (eat--prompt-prev-matching-input-str-pos regexp n)))
+    ;; Has a match been found?
+    (if (null pos)
+       (user-error "Not found")
+      (if (and eat--prompt-input-ring-index
+               restore
+               (or (and (< n 0)
+                        (< eat--prompt-input-ring-index pos))
+                   (and (> n 0)
+                        (> eat--prompt-input-ring-index pos))))
+          ;; We have a wrap; restore contents.
+          (eat-prompt-restore-input)
+        ;; If leaving the edit line, save partial input.
+        (if (null eat--prompt-input-ring-index) ;not yet on ring
+           (setq eat--prompt-stored-incomplete-input
+                 (buffer-substring-no-properties
+                   (eat-term-end eat--terminal) (point-max))))
+        (setq eat--prompt-input-ring-index pos)
+        (unless isearch-mode
+         (let ((message-log-max nil))  ; Do not write to *Messages*.
+           (message "History item: %d" (1+ pos))))
+        (eat--prompt-delete-input)
+        (insert (ring-ref eat--prompt-input-ring pos))))))
+
+(defun eat-prompt-next-matching-input (regexp n)
+  "Search forwards through input history for match for REGEXP.
+\(Later history elements are more recent commands.)
+With prefix argument N, search for Nth following match.
+If N is negative, find the previous or Nth previous match."
+  (interactive (eat--prompt-ask-for-regexp-arg
+                "Next input matching (regexp): "))
+  (eat-prompt-previous-matching-input regexp (- n)))
+
+(defun eat-prompt-previous-matching-input-from-input (n)
+  "Search backwards through input history for match for current input.
+\(Previous history elements are earlier commands.)
+With prefix argument N, search for Nth previous match.
+If N is negative, search forwards for the -Nth following match."
+  (interactive "p")
+  (let ((opoint (point)))
+    (unless (memq last-command
+                  '(eat-prompt-previous-matching-input-from-input
+                   eat-prompt-next-matching-input-from-input))
+      ;; Starting a new search
+      (setq eat--prompt-matching-input-from-input-string
+           (buffer-substring (eat-term-end eat--terminal)
+                              (point-max)))
+      (setq eat--prompt-input-ring-index nil))
+    (eat-prompt-previous-matching-input
+     (concat "^" (regexp-quote
+                  eat--prompt-matching-input-from-input-string))
+     n t)
+    (when (eq eat-prompt-move-point-for-matching-input 'after-input)
+      (goto-char opoint))))
+
+(defun eat-prompt-next-matching-input-from-input (n)
+  "Search forwards through input history for match for current input.
+\(Following history elements are more recent commands.)
+With prefix argument N, search for Nth following match.
+If N is negative, search backwards for the -Nth previous match."
+  (interactive "p")
+  (eat-prompt-previous-matching-input-from-input (- n)))
+
+(defun eat-prompt-find-input ()
+  "Find and insert input history using minibuffer."
+  (declare (interactive-only t))
+  (interactive)
+  (when (or (not (ring-p eat--prompt-input-ring))
+           (ring-empty-p eat--prompt-input-ring))
+    (user-error "No history"))
+  (let ((str (completing-read
+              "Input: "
+              (seq-uniq (ring-elements eat--prompt-input-ring)) nil
+              nil (buffer-substring (eat-term-end eat--terminal)
+                                    (point-max))))
+        (i 0)
+        (pos nil))
+    (while (and (< i (ring-length eat--prompt-input-ring)) (not pos))
+      (when (equal (ring-ref eat--prompt-input-ring i) str)
+        (setq pos i))
+      (cl-incf i))
+    (when pos
+      (setq eat--prompt-input-ring-index pos))
+    (eat--prompt-delete-input)
+    (insert str)))
+
+
 ;;;;; Major Mode.
 
 (defun eat--synchronize-scroll-windows (&optional force-selected)
@@ -5841,13 +6163,19 @@ END if it's safe to do so."
           eat--shell-prompt-begin
           eat--shell-prompt-mark
           eat--shell-prompt-mark-overlays
+          eat--prompt-mode-previous-mode
+          eat--prompt-input-ring
+          eat--prompt-input-ring-index
+          eat--prompt-stored-incomplete-input
+          eat--prompt-matching-input-from-input-string
           eat--pending-output-chunks
           eat--output-queue-first-chunk-time
           eat--process-output-queue-timer
           eat--shell-prompt-annotation-correction-timer))
   ;; This is intended; input methods don't work on read-only buffers.
   (setq buffer-read-only nil)
-  (setq buffer-undo-list t)
+  (unless eat-enable-native-shell-prompt-editing
+    (setq buffer-undo-list t))
   (setq eat--synchronize-scroll-function #'eat--synchronize-scroll)
   (setq filter-buffer-substring-function
         #'eat--filter-buffer-substring)
@@ -5886,6 +6214,21 @@ mouse-3: Switch to emacs mode"
                        (down-mouse-1 . eat-semi-char-mode)
                        (down-mouse-3 . eat-emacs-mode)))))
                  "]"))
+              (eat--prompt-mode
+               '("["
+                 (:propertize
+                  "prompt"
+                  help-echo "mouse-1: Switch to semi char mode, \
+mouse-2: Switch to emacs mode, mouse-3: Switch to char mode"
+                  mouse-face mode-line-highlight
+                  local-map
+                  (keymap
+                   (mode-line
+                    . (keymap
+                       (down-mouse-1 . eat-semi-char-mode)
+                       (down-mouse-2 . eat-emacs-mode)
+                       (down-mouse-3 . eat-char-mode)))))
+                 "]"))
               (t
                '("["
                  (:propertize
@@ -5988,32 +6331,41 @@ OS's."
   "Process the output queue on BUFFER."
   (when (buffer-live-p buffer)
     (with-current-buffer buffer
-      (let ((inhibit-quit t)            ; Don't disturb!
-            (inhibit-read-only t)
-            (inhibit-modification-hooks t)
+      (let ((inhibit-quit t)        ; Don't disturb!
             (sync-windows (eat--synchronize-scroll-windows)))
-        (when eat--process-output-queue-timer
-          (cancel-timer eat--process-output-queue-timer))
-        (setq eat--output-queue-first-chunk-time nil)
-        (let ((queue eat--pending-output-chunks))
-          (setq eat--pending-output-chunks nil)
-          (dolist (output (nreverse queue))
-            (eat-term-process-output eat--terminal output)))
-        (eat-term-redisplay eat--terminal)
-        ;; Truncate output of previous dead processes.
-        (when (and eat-term-scrollback-size
-                   (< eat-term-scrollback-size
-                      (- (point) (point-min))))
-          (delete-region
-           (point-min)
-           (max (point-min)
-                (- (eat-term-display-beginning eat--terminal)
-                   eat-term-scrollback-size))))
-        (setq eat--shell-prompt-annotation-correction-timer
-              (run-with-timer
-               eat-shell-prompt-annotation-correction-delay
-               nil #'eat--correct-shell-prompt-mark-overlays
-               buffer))
+        (save-restriction
+          (widen)
+          (let ((inhibit-read-only t)
+                (inhibit-modification-hooks t)
+                ;; Don't let `undo' mess up with the terminal.
+                (buffer-undo-list buffer-undo-list))
+            (when eat--process-output-queue-timer
+              (cancel-timer eat--process-output-queue-timer))
+            (setq eat--output-queue-first-chunk-time nil)
+            (let ((queue eat--pending-output-chunks))
+              (setq eat--pending-output-chunks nil)
+              (dolist (output (nreverse queue))
+                (eat-term-process-output eat--terminal output)))
+            (eat-term-redisplay eat--terminal)
+            ;; Truncate output of previous dead processes.
+            (when (and eat-term-scrollback-size
+                       (< eat-term-scrollback-size
+                          (- (point) (point-min))))
+              (delete-region
+               (point-min)
+               (max (point-min)
+                    (- (eat-term-display-beginning eat--terminal)
+                       eat-term-scrollback-size))))
+            (setq eat--shell-prompt-annotation-correction-timer
+                  (run-with-timer
+                   eat-shell-prompt-annotation-correction-delay
+                   nil #'eat--correct-shell-prompt-mark-overlays
+                   buffer))
+            (add-text-properties
+             (eat-term-beginning eat--terminal)
+             (eat-term-end eat--terminal)
+             '( read-only t rear-nonsticky t front-sticky t
+                field eat-terminal))))
         (funcall eat--synchronize-scroll-function sync-windows))
       (run-hooks 'eat-update-hook))))
 
@@ -6049,7 +6401,12 @@ to it."
     (when (memq (process-status process) '(signal exit))
       (if (buffer-live-p buffer)
           (with-current-buffer buffer
-            (let ((inhibit-read-only t))
+            (let ((inhibit-read-only t)
+                  ;; We're is going to write outside of the terminal,
+                  ;; so we won't synchronize buffer scroll here as we
+                  ;; will set the buffer point automatically by
+                  ;; writing to the buffer.
+                  (eat--synchronize-scroll-function #'ignore))
               (when eat--process-output-queue-timer
                 (cancel-timer eat--process-output-queue-timer)
                 (setq eat--process-output-queue-timer nil))
@@ -6065,6 +6422,11 @@ to it."
                 (setq eat--shell-prompt-begin nil)
                 (setq eat--shell-prompt-mark nil)
                 (setq eat--shell-prompt-mark-overlays nil))
+              (when eat--prompt-mode
+                (setq eat--prompt-mode-previous-mode 'dont-restore)
+                (eat--prompt-mode -1)
+                (delete-region (eat-term-end eat--terminal)
+                               (point-max)))
               (eat-emacs-mode)
               (eat-term-delete eat--terminal)
               (setq eat--terminal nil)
@@ -6141,15 +6503,8 @@ same Eat buffer.  The hook `eat-exec-hook' is run after 
each exec."
             #'eat--manipulate-kill-ring)
       (setf (eat-term-ring-bell-function eat--terminal) #'eat--bell)
       (setf (eat-term-set-cwd-function eat--terminal) #'eat--set-cwd)
-      (setf (eat-term-prompt-start-function eat--terminal)
-            #'eat--pre-prompt)
-      (setf (eat-term-prompt-end-function eat--terminal)
-            #'eat--post-prompt)
-      (setf (eat-term-set-cmd-function eat--terminal) #'eat--set-cmd)
-      (setf (eat-term-cmd-start-function eat--terminal)
-            #'eat--pre-cmd)
-      (setf (eat-term-cmd-finish-function eat--terminal)
-            #'eat--set-cmd-status)
+      (setf (eat-term-parameter eat--terminal 'ui-command-function)
+            #'eat--handle-uic)
       (setf (eat-term-parameter eat--terminal 'sixel-display-method)
             (cond ((and (display-graphic-p)
                         (image-type-available-p 'svg))
@@ -6458,6 +6813,23 @@ PROGRAM can be a shell command."
 (declare-function eshell-head-process "esh-cmd" ())
 (declare-function eshell-resume-eval "esh-cmd" ())
 
+(defun eat--eshell-handle-uic (_ cmd)
+  "Handle UI Command sequence CMD."
+  (pcase cmd
+    ;; UIC e ; A ; <t> ; <s> ST.
+    ((rx string-start "e;A;"
+         (let host (zero-or-more (not ?\;)))
+         ?\; (let path (zero-or-more anything))
+         string-end)
+     (eat--set-cwd-uic host path))
+    ;; UIC e ; F ; <t> ST.
+    ((rx string-start "e;F;"
+         (let cmd (zero-or-more anything))
+         string-end)
+     (eat--set-cmd cmd))
+    ;; Other sequences are ignored.
+    ))
+
 (defun eat--eshell-term-name (&rest _)
   "Return the value of `TERM' environment variable for Eshell."
   (eat-term-name))
@@ -6465,21 +6837,23 @@ PROGRAM can be a shell command."
 (defun eat--eshell-output-filter ()
   "Handle output from subprocess."
   (let ((inhibit-quit t)            ; Don't disturb!
-        (inhibit-read-only t)
         (str (buffer-substring-no-properties
               eshell-last-output-start
               eshell-last-output-end)))
-    (delete-region eshell-last-output-start eshell-last-output-end)
+    (let ((inhibit-read-only t))
+      (delete-region eshell-last-output-start eshell-last-output-end))
     (let ((sync-windows (eat--synchronize-scroll-windows)))
       (eat-term-process-output eat--terminal str)
       (eat-term-redisplay eat--terminal)
       (funcall eat--synchronize-scroll-function sync-windows))
-    (let ((end (eat-term-end eat--terminal)))
-      (set-marker eshell-last-output-start end)
-      (set-marker eshell-last-output-end end)
-      (set-marker (process-mark (eat-term-parameter
-                                 eat--terminal 'eat--output-process))
-                  end)))
+    (let ((inhibit-read-only t))
+      (let ((end (eat-term-end eat--terminal)))
+        (set-marker eshell-last-output-start end)
+        (set-marker eshell-last-output-end end)
+        (set-marker (process-mark
+                     (eat-term-parameter
+                      eat--terminal 'eat--output-process))
+                    end))))
   (run-hooks 'eat-eshell-update-hook))
 
 (defun eat--eshell-setup-proc-and-term (proc)
@@ -6499,7 +6873,8 @@ PROGRAM can be a shell command."
           #'eat--manipulate-kill-ring)
     (setf (eat-term-ring-bell-function eat--terminal) #'eat--bell)
     (setf (eat-term-set-cwd-function eat--terminal) #'eat--set-cwd)
-    (setf (eat-term-set-cmd-function eat--terminal) #'eat--set-cmd)
+    (setf (eat-term-parameter eat--terminal 'ui-command-function)
+          #'eat--eshell-handle-uic)
     (setf (eat-term-parameter eat--terminal 'sixel-display-method)
           (cond ((and (display-graphic-p)
                       (image-type-available-p 'svg))
@@ -6604,7 +6979,11 @@ PROGRAM can be a shell command."
   "Process status message MESSAGE from PROCESS."
   (when (buffer-live-p (process-buffer process))
     (with-current-buffer (process-buffer process)
-      (cl-letf* ((process-send-string
+      ;; Eshell is going to write outside of the terminal, so we won't
+      ;; synchronize buffer scroll here as it'll interfare with
+      ;; Eshell.
+      (cl-letf* ((eat--synchronize-scroll-function #'ignore)
+                 (process-send-string
                   (symbol-function #'process-send-string))
                  ((symbol-function #'process-send-string)
                   (lambda (proc string)
diff --git a/integration/bash b/integration/bash
index bfe18edae2..993f229659 100644
--- a/integration/bash
+++ b/integration/bash
@@ -102,6 +102,17 @@ __eat_enable_integration ()
   done
   # Step 2.2: Assign the first element.
   PROMPT_COMMAND[0]=__eat_before_prompt_command
+  # Send the history, for native shell prompt.
+  printf '\e]51;e;I;0;%s;%s\e\\' \
+         "$(printf "%s" "$HOSTNAME" | base64)" \
+         "$(printf "%s" "$HISTFILE" | base64)"
+  local REPLY
+  IFS=$';\e' read -r -s -t 10 -d "\\" -a REPLY
+  if test "${REPLY[4]}" != 0
+  then
+    printf '\e]51;e;I;1;%s\e\\' \
+           "$(tail -n "${REPLY[4]}" "$HISTFILE" | base64)"
+  fi
 }
 
 # Enable.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]