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

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

[nongnu] elpa/eat a04999011f 3/9: Annotate shell prompts


From: ELPA Syncer
Subject: [nongnu] elpa/eat a04999011f 3/9: Annotate shell prompts
Date: Tue, 6 Dec 2022 13:58:46 -0500 (EST)

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

    Annotate shell prompts
    
    * eat.el (eat-enable-shell-prompt-annotation)
    (eat-shell-prompt-annotation-position)
    (eat-shell-prompt-annotation-running-margin-indicator)
    (eat-shell-prompt-annotation-success-margin-indicator)
    (eat-shell-prompt-annotation-failure-margin-indicator)
    (eat-shell-prompt-annotation-delay): New user option.
    * eat.el (eat-shell-prompt-annotation-running)
    (eat-shell-prompt-annotation-success)
    (eat-shell-prompt-annotation-failure): New face.
    * eat.el (eat--shell-command-status, eat--shell-prompt-begin)
    (eat--shell-prompt-mark, eat--shell-prompt-mark-overlays): New
    variable.
    * eat.el (eat--pre-prompt, eat--post-prompt)
    (eat--update-shell-prompt-mark-overlays, eat--pre-cmd)
    (eat--set-cmd-status): New function.
    * eat.el (eat-mode): Make 'eat--shell-command-status',
    'eat--shell-prompt-begin', 'eat--shell-prompt-mark',
    'eat--shell-prompt-mark-overlays' and
    'eat--shell-prompt-annotation-update-timer' local.  Set margin
    width if shell prompt annotation is enabled.
    * eat.el (eat--shell-prompt-annotation-update-timer): New
    variable.
    * eat.el (eat--process-output-queue): Call or schedule
    'eat--update-shell-prompt-mark-overlays' call.
    * eat.el (eat--filter): Cancel timer
    'eat--shell-prompt-annotation-update-timer'.
    * eat.el (eat-exec): Set prompt start & end and command start &
    end functions.
---
 eat.el | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 235 insertions(+), 17 deletions(-)

diff --git a/eat.el b/eat.el
index 2c2908b80a..824277e028 100644
--- a/eat.el
+++ b/eat.el
@@ -161,6 +161,63 @@ the history of commands like `eat', `shell-command' and
   :group 'eat-ui
   :group 'eat-eshell)
 
+(defcustom eat-enable-shell-prompt-annotation t
+  "Non-nil means annotate shell prompt with the status of command.
+
+When non-nil, display a mark in front of shell prompt describing the
+status of the command executed in that prompt."
+  :type 'boolean
+  :group 'eat-ui)
+
+(defcustom eat-shell-prompt-annotation-position 'left-margin
+  "The position where to display shell prompt annotation.
+
+The value can be one of the following:
+
+`left-margin'   Use the left margin.
+`right-margin'  Use the right margin."
+  :type '(choice (const :tag "Left margin" left-margin)
+                 (const :tag "Right margin" right-margin))
+  :group 'eat-ui)
+
+(defcustom eat-shell-prompt-annotation-running-margin-indicator "-"
+  "String in margin annotation to indicate the command is running."
+  :type 'string
+  :group 'eat-ui)
+
+(defface eat-shell-prompt-annotation-running
+  '((t :inherit compilation-info))
+  "Face used in annotation to indicate the command is running."
+  :group 'eat-ui)
+
+(defcustom eat-shell-prompt-annotation-success-margin-indicator "0"
+  "String in margin annotation to indicate the command has succeeded."
+  :type 'string
+  :group 'eat-ui)
+
+(defface eat-shell-prompt-annotation-success
+  '((t :inherit success))
+  "Face used in annotation to indicate the command has succeeded."
+  :group 'eat-ui)
+
+(defcustom eat-shell-prompt-annotation-failure-margin-indicator "X"
+  "String in margin annotation to indicate the command has failed."
+  :type 'string
+  :group 'eat-ui)
+
+(defface eat-shell-prompt-annotation-failure
+  '((t :inherit error))
+  "Face used in annotation to indicate the command has failed."
+  :group 'eat-ui)
+
+(defcustom eat-shell-prompt-annotation-delay 0.1
+  "Seconds to wait before updating prompt annotations.
+
+Nil means update immediately."
+  :type '(choice (const :tag "Immediately" nil)
+                 number)
+  :group 'eat-ui)
+
 (defconst eat--cursor-type-value-type
   (let ((cur-type
          '(choice
@@ -4112,6 +4169,18 @@ return \"eat-color\", otherwise return \"eat-mono\"."
 (defvar eat--synchronize-scroll-function nil
   "Function to synchronize scrolling between terminal and window.")
 
+(defvar eat--shell-command-status 0
+  "If the current shell command has finished, its exit status.")
+
+(defvar eat--shell-prompt-begin nil
+  "Beginning of last shell prompt.")
+
+(defvar eat--shell-prompt-mark nil
+  "Display property used to put a mark before the previous prompt.")
+
+(defvar eat--shell-prompt-mark-overlays nil
+  "List of overlay used to put marks before shell prompts.")
+
 (defun eat-reset ()
   "Perform a terminal reset."
   (interactive)
@@ -4170,11 +4239,118 @@ If HOST isn't the host Emacs is running on, don't do 
anything."
     (ignore-errors
       (cd-absolute cwd))))
 
+(defun eat--pre-prompt (_)
+  "Save the beginning position of shell prompt."
+  (when eat-enable-shell-prompt-annotation
+    (setq eat--shell-prompt-begin (point-marker))))
+
+(defun eat--post-prompt (_)
+  "Put a mark in the marginal area on current line."
+  (when eat-enable-shell-prompt-annotation
+    (let ((indicator
+           (if (zerop eat--shell-command-status)
+               (propertize
+                eat-shell-prompt-annotation-success-margin-indicator
+                'face 'eat-shell-prompt-annotation-success)
+             (propertize
+              eat-shell-prompt-annotation-failure-margin-indicator
+              'face 'eat-shell-prompt-annotation-failure))))
+      ;; Update previous prompt's indicator using side-effect.
+      (when eat--shell-prompt-mark
+        (setf (cadr eat--shell-prompt-mark) indicator)
+        (setq eat--shell-prompt-mark nil))
+      ;; Show this prompt's indicator.
+      (when eat--shell-prompt-begin
+        (when (< eat--shell-prompt-begin (point))
+          ;; Save it, we'll use side-effect.
+          (setq eat--shell-prompt-mark
+                `((margin ,eat-shell-prompt-annotation-position)
+                  ,indicator))
+          ;; Make overlay and put bookkeeping properties.
+          (let ((identifier (gensym "eat--prompt-mark-identifier-"))
+                (before-str
+                 (propertize " " 'display eat--shell-prompt-mark))
+                (ov (make-overlay eat--shell-prompt-begin
+                                  (1+ eat--shell-prompt-begin))))
+            (overlay-put ov 'before-string before-str)
+            (overlay-put ov 'eat--shell-prompt-mark-id identifier)
+            (add-text-properties
+             eat--shell-prompt-begin (1+ eat--shell-prompt-begin)
+             (list 'eat--before-string before-str
+                   'eat--shell-prompt-mark-id identifier
+                   'eat--shell-prompt-mark-overlay ov))
+            (push ov eat--shell-prompt-mark-overlays)))
+        (setq eat--shell-prompt-begin nil)))))
+
+(defun eat--update-shell-prompt-mark-overlays (buffer)
+  "Update all overlays used to add mark before shell prompt.
+
+BUFFER is the terminal buffer."
+  (when (and (buffer-live-p buffer)
+             eat-enable-shell-prompt-annotation)
+    (with-current-buffer buffer
+      (while-no-input
+        ;; Delete all outdated overlays.
+        (dolist (ov eat--shell-prompt-mark-overlays)
+          (unless (eq (overlay-get ov 'eat--shell-prompt-mark-id)
+                      (get-text-property (overlay-start ov)
+                                         'eat--shell-prompt-mark-id))
+            (delete-overlay ov)
+            (setq eat--shell-prompt-mark-overlays
+                  (delq ov eat--shell-prompt-mark-overlays))))
+        (save-excursion
+          ;; Recreate overlays if needed.
+          (goto-char (eat-term-beginning eat--terminal))
+          (while (< (point) (eat-term-end eat--terminal))
+            (when (get-text-property
+                   (point) 'eat--shell-prompt-mark-id)
+              (let ((ov (get-text-property
+                         (point) 'eat--shell-prompt-mark-overlay)))
+                (unless
+                    (and ov
+                         (overlay-buffer ov)
+                         (eq (overlay-get
+                              ov 'eat--shell-prompt-mark-id)
+                             (get-text-property
+                              (point) 'eat--shell-prompt-mark-id)))
+                  ;; Recreate.
+                  (when ov
+                    (delete-overlay ov)
+                    (setq eat--shell-prompt-mark-overlays
+                          (delq ov eat--shell-prompt-mark-overlays)))
+                  (setq ov (make-overlay (point) (1+ (point))))
+                  (overlay-put ov 'before-string
+                               (get-text-property
+                                (point) 'eat--before-string))
+                  (overlay-put ov 'eat--shell-prompt-mark-id
+                               (get-text-property
+                                (point) 'eat--shell-prompt-mark-id))
+                  (push ov eat--shell-prompt-mark-overlays))))
+            (goto-char (or (next-single-property-change
+                            (point) 'eat--shell-prompt-mark-id nil
+                            (eat-term-end eat--terminal))
+                           (eat-term-end eat--terminal)))))))))
+
 (defun eat--set-cmd (_ cmd)
   "Add CMD to `shell-command-history'."
   (when eat-enable-shell-command-history
     (add-to-history 'shell-command-history cmd)))
 
+(defun eat--pre-cmd (_)
+  "Update shell prompt mark to indicate command is running."
+  (when (and eat-enable-shell-prompt-annotation
+             eat--shell-prompt-mark)
+    (setf (cadr eat--shell-prompt-mark)
+          (propertize
+           eat-shell-prompt-annotation-running-margin-indicator
+           'face 'eat-shell-prompt-annotation-running))))
+
+(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)))
+
 
 ;;;;; Input.
 
@@ -4546,21 +4722,27 @@ END if it's safe to do so."
 (define-derived-mode eat-mode fundamental-mode "Eat"
   "Major mode for Eat."
   :group 'eat-ui
-  (mapc #'make-local-variable '(buffer-read-only
-                                buffer-undo-list
-                                filter-buffer-substring-function
-                                mode-line-process
-                                mode-line-buffer-identification
-                                glyphless-char-display
-                                cursor-type
-                                track-mouse
-                                eat--terminal
-                                eat--process
-                                eat--synchronize-scroll-function
-                                eat--mouse-grabbing-type
-                                eat--pending-output-chunks
-                                eat--output-queue-first-chunk-time
-                                eat--process-output-queue-timer))
+  (mapc #'make-local-variable
+        '(buffer-read-only
+          buffer-undo-list
+          filter-buffer-substring-function
+          mode-line-process
+          mode-line-buffer-identification
+          glyphless-char-display
+          cursor-type
+          track-mouse
+          eat--terminal
+          eat--process
+          eat--synchronize-scroll-function
+          eat--mouse-grabbing-type
+          eat--shell-command-status
+          eat--shell-prompt-begin
+          eat--shell-prompt-mark
+          eat--shell-prompt-mark-overlays
+          eat--pending-output-chunks
+          eat--output-queue-first-chunk-time
+          eat--process-output-queue-timer
+          eat--shell-prompt-annotation-update-timer))
   ;; This is intended; input methods don't work on read-only buffers.
   (setq buffer-read-only nil
         buffer-undo-list t
@@ -4634,7 +4816,24 @@ mouse-3: Switch to char mode"
   ;; that would break the display.
   (eat--setup-glyphless-chars)
   (when eat-enable-blinking-text
-    (eat-blink-mode +1)))
+    (eat-blink-mode +1))
+  (when eat-enable-shell-prompt-annotation
+    (let ((margin-width
+           (max
+            (string-width
+             eat-shell-prompt-annotation-running-margin-indicator)
+            (string-width
+             eat-shell-prompt-annotation-success-margin-indicator)
+            (string-width
+             eat-shell-prompt-annotation-failure-margin-indicator))))
+      (pcase-exhaustive eat-shell-prompt-annotation-position
+        ('left-margin
+         (setq left-margin-width margin-width))
+        ('right-margin
+         (setq right-margin-width margin-width))))
+    ;; Make sure the marginal area is resized.
+    (dolist (win (get-buffer-window-list))
+      (set-window-buffer win (current-buffer)))))
 
 
 ;;;;; Process Handling.
@@ -4653,6 +4852,9 @@ The output chunks are pushed, so last output appears 
first.")
 (defvar eat--process-output-queue-timer nil
   "Timer to process output queue.")
 
+(defvar eat--shell-prompt-annotation-update-timer nil
+  "Timer to update shell prompt annotations.")
+
 (defun eat-kill-process ()
   "Kill Eat process in current buffer."
   (interactive)
@@ -4708,6 +4910,13 @@ OS's."
            (max (point-min)
                 (- (eat-term-display-beginning eat--terminal)
                    eat-term-scrollback-size))))
+        (if (null eat-shell-prompt-annotation-delay)
+            (eat--update-shell-prompt-mark-overlays buffer)
+          (setq eat--shell-prompt-annotation-update-timer
+                (run-with-timer
+                 eat-shell-prompt-annotation-delay
+                 nil #'eat--update-shell-prompt-mark-overlays
+                 buffer)))
         (when synchronize-scroll
           (funcall eat--synchronize-scroll-function))))))
 
@@ -4717,6 +4926,8 @@ OS's."
     (with-current-buffer (process-buffer process)
       (when eat--process-output-queue-timer
         (cancel-timer eat--process-output-queue-timer))
+      (when eat--shell-prompt-annotation-update-timer
+        (cancel-timer eat--shell-prompt-annotation-update-timer))
       (unless eat--output-queue-first-chunk-time
         (setq eat--output-queue-first-chunk-time (current-time)))
       (push output eat--pending-output-chunks)
@@ -4816,7 +5027,14 @@ same Eat buffer.  The hook `eat-exec-hook' is run after 
each exec."
             #'eat--manipulate-kill-ring
             (eat-term-ring-bell-function eat--terminal) #'eat--bell
             (eat-term-set-cwd-function eat--terminal) #'eat--set-cwd
-            (eat-term-set-cmd-function eat--terminal) #'eat--set-cmd)
+            (eat-term-prompt-start-function eat--terminal)
+            #'eat--pre-prompt
+            (eat-term-prompt-end-function eat--terminal)
+            #'eat--post-prompt
+            (eat-term-set-cmd-function eat--terminal) #'eat--set-cmd
+            (eat-term-cmd-start-function eat--terminal) #'eat--pre-cmd
+            (eat-term-cmd-finish-function eat--terminal)
+            #'eat--set-cmd-status)
       ;; Crank up a new process.
       (let* ((size (eat-term-size eat--terminal))
              (process-environment



reply via email to

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