From: Ken Manheimer Date: Sat, 28 Nov 2015 21:10:00 +0000 (-0500) Subject: poptoshell - Implement and document remote shell feature. X-Git-Url: https://code.delx.au/gnu-emacs-elpa/commitdiff_plain/c62525e675ca59d52c07fe9f46ae74795086e0cc poptoshell - Implement and document remote shell feature. --- diff --git a/poptoshell.el b/poptoshell.el index b9cba193f..fe9a8ae8a 100644 --- a/poptoshell.el +++ b/poptoshell.el @@ -34,44 +34,75 @@ Otherwise, open a new window in the current frame." (defun pop-to-shell (&optional arg) - "Navigate to or within a shell buffer. + "Navigate to or within shell buffers. -Use this command from within a shell subprocess buffer to get to -the shell input point, or from outside a shell buffer to pop to a -shell buffer, without displacing the current buffer. +Use universal arguments to choose between alternate shell +buffers, select which is default, and using Emacs tramp syntax, +launch or return to a remote shell. -Specifically, like 'shell' command but: +Basic operation: - - If the current buffer is associated with a subprocess (and one not - among those named on `non-interactive-process-buffers'), then focus - is moved to the process input point, else... - - Goes to a window that is already showing a shell buffer, if any. - In this case, the cursor is left in its prior position in the shell - buffer. (Repeating the command will then go to the process input - point, by the behavior mentioned just before this.) Else... - - Pops open a new shell buffer, if none is around. + - If the current buffer is associated with a subprocess (that is + not among those named on `non-interactive-process-buffers'), + then focus is moved to the process input point. -In any cases where the shell buffer already existed, the process -is resumed if it was stopped. + \(You can use a universal argument go to a different shell + buffer when already in a buffer that has a process - see + below.) -Further, + - If not in a shell buffer (or with universal argument), go to a + window that is already showing the (a) shell buffer, if any. - - With a universal argument, the user is prompted for the buffer name to - use (it will be bracketed by asterisks - a regrettable comint - requirement), defaulting to 'shell'. This makes it easy to switch - between multiple process-associated buffers. - - A double universal argument will set the default target shell buffer name - to the provided one, making the target shell subsequently primary." + In this case, the cursor is left in its prior position in the + shell buffer. (Repeating the command will then go to the + process input point, by the previous behavior.) + + - Otherwise, start a new shell buffer, using the current + directory as the working directory.. + +If the resulting buffer's shell process was stopped or +terminated, it's resumed. + +Use the universal arg to start and select between named shell buffers: + +You can name alternate shell buffers to create or return to using +one or two universal arguments: + + - With a single universal argument, prompt for the buffer name + to use (without the asterisks that shell mode will put around + the name), defaulting to 'shell'. + + Completion is available. + + This combination makes it easy to start and switch between + multiple shell buffers. + + - A double universal argument will prompt for the name *and* set + the default to that name, making the target shell subsequently + primary. + + +Launching remote shells: + +When using a universal arg to specify a shell buffer name, you +can include a tramp-style remote specification before the name +to start a remote shell. + +For example: '/ssh:myriadicity.net:/myr' or +'/ssh:myriadicity.net|sudo:root@myriadicity.net:/\#myr', etc. +The stuff between the '/' slashes will be used for +starting the remote shell, and the stuff after the second +slash will be used for the shell name." (interactive "P") (if (not (boundp 'shell-buffer-name)) (setq shell-buffer-name "*shell*")) - (let* ((from (current-buffer)) + (let* ((from-buffer (current-buffer)) (doublearg (equal arg '(16))) (temp (if arg - (read-shell-buffer-name-sans + (read-bare-shell-buffer-name (format "Shell buffer name [%s]%s " (substring-no-properties pop-to-shell-primary-name @@ -88,15 +119,15 @@ Further, (setq use-default-dir (match-string 1 temp)) (bracket-asterisks (match-string 2 temp))) (t (bracket-asterisks temp)))) - (curr-buff-proc (or (get-buffer-process from) + (curr-buff-proc (or (get-buffer-process from-buffer) (and (fboundp 'rcirc-buffer-process) (rcirc-buffer-process)) (and (boundp 'erc-process) erc-process))) - (buff (if (and curr-buff-proc - (not (member (buffer-name from) + (target-buffer (if (and curr-buff-proc + (not (member (buffer-name from-buffer) non-interactive-process-buffers))) - from + from-buffer (get-buffer target-shell-buffer-name))) (inwin nil) (num 0) @@ -105,45 +136,35 @@ Further, (when doublearg (setq pop-to-shell-primary-name target-shell-buffer-name)) - (if (and curr-buff-proc - (not arg) - (eq from buff) - (not (eq target-shell-buffer-name (buffer-name from)))) - ;; We're in a buffer with a shell process, but not named shell - ;; - stick with it, but go to end: - (setq already-there t) - (cond - ; Already in the shell buffer: - ((string= (buffer-name) target-shell-buffer-name) - (setq already-there t)) - ((or (not buff) - (not (catch 'got-a-vis - (my-walk-windows - (function (lambda (win) - (if (and (eq (window-buffer win) buff) - (equal (frame-parameter - (selected-frame) 'display) - (frame-parameter - (window-frame win) 'display))) - (progn (setq inwin win) - (throw 'got-a-vis win)) - (setq num (1+ num))))) - nil 'visible t) - nil))) - ;; No preexisting shell buffer, or not in a visible window: - (pop-to-buffer target-shell-buffer-name pop-up-windows)) + ;; Situate: + + (cond + + ((and curr-buff-proc + (not arg) + (eq from-buffer target-buffer) + (not (eq target-shell-buffer-name (buffer-name from-buffer)))) + ;; In a shell buffer, but not named - stay in buffer, but go to end. + (setq already-there t)) + + ((string= (buffer-name) target-shell-buffer-name) + ;; Already in the specified shell buffer: + (setq already-there t)) + + ((or (not target-buffer) + (not (pop-to-shell:visible target-buffer))) + ;; No preexisting shell buffer, or not in a visible window: + (pop-to-buffer target-shell-buffer-name pop-up-windows)) + ;; Buffer exists and already has a window - jump to it: - (t (if (and pop-to-shell-frame - inwin - (not (equal (window-frame (selected-window)) - (window-frame inwin)))) - (select-frame-set-input-focus (window-frame inwin))) - (if (not (string= (buffer-name (current-buffer)) - target-shell-buffer-name)) - (pop-to-buffer target-shell-buffer-name t)))) - (if (not (comint-check-proc (current-buffer))) - (start-shell-in-buffer (buffer-name (current-buffer)))) - ) + (t (if (and pop-to-shell-frame + inwin + (not (equal (window-frame (selected-window)) + (window-frame inwin)))) + (select-frame-set-input-focus (window-frame inwin))) + (if (not (string= (buffer-name (current-buffer)) + target-shell-buffer-name)) + (pop-to-buffer target-shell-buffer-name t)))) ;; We're in the buffer. @@ -151,36 +172,45 @@ Further, (when use-default-dir (cd use-default-dir)) + ;; Activate: + + (if (not (comint-check-proc (current-buffer))) + (start-shell-in-buffer (buffer-name (current-buffer)))) + ;; If the destination buffer has a stopped process, resume it: (let ((process (get-buffer-process (current-buffer)))) (if (and process (equal 'stop (process-status process))) (continue-process process))) (if (and (not already-there) - (not (equal (current-buffer) from))) + (not (equal (current-buffer) from-buffer))) t (goto-char (point-max)) - (and (get-buffer-process from) - (goto-char (process-mark (get-buffer-process from))))) + (and (get-buffer-process from-buffer) + (goto-char (process-mark (get-buffer-process from-buffer))))) ) ) -(defun my-walk-windows (func &optional minibuf all-frames selected) - (if (featurep 'xemacs) - (walk-windows func minibuf all-frames (selected-device)) - (walk-windows func minibuf all-frames))) - -(defun my-set-mouse-position (window x y) - "Adapt for both xemacs and fsf emacs" - (if (string= (substring (emacs-version) 0 6) "XEmacs") - (set-mouse-position window x y) - (let ((frame (window-frame window))) - (select-frame-set-input-focus frame)))) - +(defun pop-to-shell:visible (buffer) + "True if buffer is in a visible window." + (catch 'got-a-vis + (walk-windows + (function (lambda (win) + (if (and (eq (window-buffer win) buffer) + (equal (frame-parameter + (selected-frame) 'display) + (frame-parameter + (window-frame win) 'display))) + (progn (setq inwin win) + (throw 'got-a-vis win)) + (setq num (1+ num))))) + nil 'visible) + nil)) -(defun read-shell-buffer-name-sans (prompt default) - "Obtain name without asterisks of shell buffer, adding the asterisks. +(defun read-bare-shell-buffer-name (prompt default) + "PROMPT for shell buffer name, sans asterisks. -Return indicated default on empty input." +Return the supplied name bracketed with the asterisks, or specified DEFAULT +on empty input." (let ((got (completing-read prompt @@ -206,7 +236,7 @@ Return indicated default on empty input." (setq name (concat name "*"))) name) (defun unbracket-asterisks (name) - "Return a copy of name, removing asterisks at beg and end, if any." + "Return a copy of name, removing asterisks, if any, at beginning and end." (if (string= (substring name 0 1) "*") (setq name (substring name 1))) (if (string= (substring name -1) "*")