]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow not erase output buffer in shell commands
authorTino Calancha <tino.calancha@gmail.com>
Tue, 16 Aug 2016 09:18:44 +0000 (18:18 +0900)
committerTino Calancha <tino.calancha@gmail.com>
Tue, 16 Aug 2016 09:18:44 +0000 (18:18 +0900)
* lisp/simple.el (shell-command-not-erase-buffer): New option to allow
not erasing the output buffer between shell commands.  Defaults to nil.
(shell-command-on-region): Use it.
(shell-command--save-pos-or-erase): New defun; store a buffer position
if 'shell-command-not-erase-buffer' is non-nil; otherwise
erase the output buffer of the shell command.
(shell-command, shell-command-on-region): Use it.
(shell-command--set-point-after-cmd): New defun;
if 'shell-command-not-erase-buffer' is non-nil, set point
in the output buffer to the position in 'shell-command-saved-pos'.
(shell-command-sentinel, shell-command-on-region): Use it.
* doc/emacs/misc.texi (shell-command-not-erase-buffer):
Document this feature in the manual.
; * etc/NEWS: Add entry for this new feature.
See discussion on:
http://lists.gnu.org/archive/html/emacs-devel/2016-07/msg00610.html

doc/emacs/misc.texi
etc/NEWS
lisp/simple.el

index 94e1f198f2b6869f7c4eeb2ae3e93b5dd9bd7517..acddb7a8f70105a31423f9a07a8e7a4da5d1dfec 100644 (file)
@@ -771,6 +771,14 @@ the output buffer.  But if you change the value of the variable
 @code{shell-command-default-error-buffer} to a string, error output is
 inserted into a buffer of that name.
 
+@vindex shell-command-not-erase-buffer
+  By default, the output buffer is erased between shell commands.
+If you change the value of the variable
+@code{shell-command-not-erase-buffer} to a non-@code{nil} value,
+the output buffer is not erased.  This variable also controls where to
+set the point in the output buffer after the command completes; see the
+documentation of the variable for details.
+
 @node Interactive Shell
 @subsection Interactive Subshell
 
index d62dcacbe98d00120a6c7a1b47a4d725b98c0149..8a13d525450a81e190f60063fb771f0d177fce19 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -55,6 +55,16 @@ affected by this, as SGI stopped supporting IRIX in December 2013.
 \f
 * Changes in Emacs 25.2
 
++++
+** The new user option 'shell-command-not-erase-buffer' controls
+if the output buffer is erased between shell commands; if non-nil,
+the output buffer is not erased; this variable also controls where
+to set the point in the output buffer: beginning of the output,
+end of the buffer or save the point.
+When 'shell-command-not-erase-buffer' is nil, the default value,
+the behaviour of 'shell-command', 'shell-command-on-region' and
+'async-shell-command' is as usual.
+
 +++
 ** The new user option 'mouse-select-region-move-to-beginning'
 controls the position of point when double-clicking mouse-1 on the end
index e91b6e02a9cb16a1c6870cd31526dd6456710389..f77c9f88557932283fc7102c1eea3a872f3ac86d 100644 (file)
 (defvar compilation-current-error)
 (defvar compilation-context-lines)
 
+(defcustom shell-command-not-erase-buffer nil
+  "If non-nil, output buffer is not erased between shell commands.
+Also, a non-nil value set the point in the output buffer
+once the command complete.
+The value `beg-last-out' set point at the beginning of the output,
+`end-last-out' set point at the end of the buffer, `save-point'
+restore the buffer position before the command."
+  :type '(choice
+          (const :tag "Erase buffer" nil)
+          (const :tag "Set point to beginning of last output" beg-last-out)
+          (const :tag "Set point to end of last output" end-last-out)
+          (const :tag "Save point" save-point))
+  :group 'shell
+  :version "25.2")
+
+(defvar shell-command-saved-pos nil
+  "Point position in the output buffer after command complete.
+It is an alist (BUFFER . POS), where BUFFER is the output
+buffer, and POS is the point position in BUFFER once the command finish.
+This variable is used when `shell-command-not-erase-buffer' is non-nil.")
+
 (defcustom idle-update-delay 0.5
   "Idle time delay before updating various things on the screen.
 Various Emacs features that update auxiliary information when point moves
@@ -3210,6 +3231,53 @@ output buffer and running a new command in the default buffer,
   :group 'shell
   :version "24.3")
 
+(defun shell-command--save-pos-or-erase ()
+  "Store a buffer position or erase the buffer.
+See `shell-command-not-erase-buffer'."
+  (let ((sym shell-command-not-erase-buffer)
+        pos)
+    (setq buffer-read-only nil)
+    ;; Setting buffer-read-only to nil doesn't suffice
+    ;; if some text has a non-nil read-only property,
+    ;; which comint sometimes adds for prompts.
+    (setq pos
+          (cond ((eq sym 'save-point) (point))
+                ((eq sym 'beg-last-out) (point-max))
+                ((not sym)
+                 (let ((inhibit-read-only t))
+                   (erase-buffer) nil))))
+    (when pos
+      (goto-char (point-max))
+      (push (cons (current-buffer) pos)
+            shell-command-saved-pos))))
+
+(defun shell-command--set-point-after-cmd (&optional buffer)
+  "Set point in BUFFER after command complete.
+BUFFER is the output buffer of the command; if nil, then defaults
+to the current BUFFER.
+Set point to the `cdr' of the element in `shell-command-saved-pos'
+whose `car' is BUFFER."
+  (when shell-command-not-erase-buffer
+    (let* ((sym  shell-command-not-erase-buffer)
+           (buf  (or buffer (current-buffer)))
+           (pos  (alist-get buf shell-command-saved-pos)))
+      (setq shell-command-saved-pos
+            (assq-delete-all buf shell-command-saved-pos))
+      (when (buffer-live-p buf)
+        (let ((win   (car (get-buffer-window-list buf)))
+              (pmax  (with-current-buffer buf (point-max))))
+          (unless (and pos (memq sym '(save-point beg-last-out)))
+            (setq pos pmax))
+          ;; Set point in the window displaying buf, if any; otherwise
+          ;; display buf temporary in selected frame and set the point.
+          (if win
+              (set-window-point win pos)
+            (save-window-excursion
+              (let ((win (display-buffer
+                          buf
+                          '(nil (inhibit-switch-frame . t)))))
+                (set-window-point win pos)))))))))
+
 (defun async-shell-command (command &optional output-buffer error-buffer)
   "Execute string COMMAND asynchronously in background.
 
@@ -3271,7 +3339,8 @@ Noninteractive callers can specify coding systems by binding
 The optional second argument OUTPUT-BUFFER, if non-nil,
 says to put the output in some other buffer.
 If OUTPUT-BUFFER is a buffer or buffer name, erase that buffer
-and insert the output there.
+and insert the output there; a non-nil value of
+`shell-command-not-erase-buffer' prevent to erase the buffer.
 If OUTPUT-BUFFER is not a buffer and not nil, insert the output
 in current buffer after point leaving mark after it.
 This cannot be done asynchronously.
@@ -3408,13 +3477,8 @@ the use of a shell (with its need to quote arguments)."
                    (setq buffer (get-buffer-create
                                  (or output-buffer "*Async Shell Command*"))))))
                (with-current-buffer buffer
-                 (setq buffer-read-only nil)
-                 ;; Setting buffer-read-only to nil doesn't suffice
-                 ;; if some text has a non-nil read-only property,
-                 ;; which comint sometimes adds for prompts.
-                 (let ((inhibit-read-only t))
-                   (erase-buffer))
                  (display-buffer buffer '(nil (allow-no-window . t)))
+                  (shell-command--save-pos-or-erase)
                  (setq default-directory directory)
                  (setq proc (start-process "Shell" buffer shell-file-name
                                            shell-command-switch command))
@@ -3497,12 +3561,14 @@ and are only used if a pop-up buffer is displayed."
 
 
 ;; We have a sentinel to prevent insertion of a termination message
-;; in the buffer itself.
+;; in the buffer itself, and to set the point in the buffer when
+;; `shell-command-not-erase-buffer' is non-nil.
 (defun shell-command-sentinel (process signal)
-  (if (memq (process-status process) '(exit signal))
-      (message "%s: %s."
-              (car (cdr (cdr (process-command process))))
-              (substring signal 0 -1))))
+  (when (memq (process-status process) '(exit signal))
+    (shell-command--set-point-after-cmd (process-buffer process))
+    (message "%s: %s."
+             (car (cdr (cdr (process-command process))))
+             (substring signal 0 -1))))
 
 (defun shell-command-on-region (start end command
                                      &optional output-buffer replace
@@ -3536,7 +3602,8 @@ appears at the end of the output.
 
 Optional fourth arg OUTPUT-BUFFER specifies where to put the
 command's output.  If the value is a buffer or buffer name,
-erase that buffer and insert the output there.
+erase that buffer and insert the output there; a non-nil value of
+`shell-command-not-erase-buffer' prevent to erase the buffer.
 If the value is nil, use the buffer `*Shell Command Output*'.
 Any other non-nil value means to insert the output in the
 current buffer after START.
@@ -3616,7 +3683,10 @@ interactively, this is t."
         (let ((buffer (get-buffer-create
                        (or output-buffer "*Shell Command Output*"))))
           (unwind-protect
-              (if (eq buffer (current-buffer))
+              (if (and (eq buffer (current-buffer))
+                       (or (not shell-command-not-erase-buffer)
+                           (and (not (eq buffer (get-buffer "*Shell Command Output*")))
+                                (not (region-active-p)))))
                   ;; If the input is the same buffer as the output,
                   ;; delete everything but the specified region,
                   ;; then replace that region with the output.
@@ -3635,10 +3705,9 @@ interactively, this is t."
                 ;; output there.
                 (let ((directory default-directory))
                   (with-current-buffer buffer
-                    (setq buffer-read-only nil)
                     (if (not output-buffer)
                         (setq default-directory directory))
-                    (erase-buffer)))
+                    (shell-command--save-pos-or-erase)))
                 (setq exit-status
                       (call-process-region start end shell-file-name nil
                                            (if error-file
@@ -3656,8 +3725,10 @@ interactively, this is t."
                            (format " - Exit [%d]" exit-status)))))
             (if (with-current-buffer buffer (> (point-max) (point-min)))
                 ;; There's some output, display it
-                (display-message-or-buffer buffer)
-              ;; No output; error?
+                (progn
+                  (display-message-or-buffer buffer)
+                  (shell-command--set-point-after-cmd buffer))
+            ;; No output; error?
               (let ((output
                      (if (and error-file
                               (< 0 (nth 7 (file-attributes error-file))))