branch: master commit 6bc713541191d94282cd418f196cfe6d38473f0e Merge: 6298b25 6cbf082 Author: Ken Manheimer <ken.manhei...@gmail.com> Commit: Ken Manheimer <ken.manhei...@gmail.com>
Merge multishell 1.1.3 --- packages/multishell/multishell-list.el | 165 ++++++++++++++++++++++---------- packages/multishell/multishell.el | 14 ++- 2 files changed, 127 insertions(+), 52 deletions(-) diff --git a/packages/multishell/multishell-list.el b/packages/multishell/multishell-list.el index b4ebe47..69299f2 100644 --- a/packages/multishell/multishell-list.el +++ b/packages/multishell/multishell-list.el @@ -3,7 +3,7 @@ ;; Copyright (C) 2016 Free Software Foundation, Inc. and Ken Manheimer ;; Author: Ken Manheimer <ken.manhei...@gmail.com> -;; Version: 1.1.2 +;; Version: 1.1.3 ;; Created: 2016 -- first public availability ;; Keywords: processes ;; URL: https://github.com/kenmanheimer/EmacsMultishell @@ -12,36 +12,62 @@ (require 'tabulated-list) -(defun multishell-list-open-pop () - "Pop to current entry's shell, and refresh the listing buffer." - (interactive) - (let ((list-buffer (current-buffer))) - (multishell-pop-to-shell nil (tabulated-list-get-id)) +(defgroup multishell-list nil + "Show a menu of all shell buffers in a buffer." + :group 'multishell) + +(defface multishell-list-name + '((t (:weight bold))) + "Face for shell names in the Multishell List." + :group 'multishell-list) + +(defun multishell-list-open-pop (&optional arg) + "Pop to current entry's shell in separate window. + +The shell is started if it's not already going, unless this is +invoked with optional `universal-argument'. In that case we +pop to the buffer but don't change its run state." + (interactive "P") + (let ((list-buffer (current-buffer)) + (entry (tabulated-list-get-id))) + (if arg + (pop-to-buffer + (multishell-bracket (multishell-name-from-entry entry))) + (multishell-pop-to-shell nil entry)) (with-current-buffer list-buffer - (revert-buffer)))) + (revert-buffer) + (multishell-list-goto-item-by-entry entry)))) + (defun multishell-list-open-as-default () "Pop to current entry's shell, and set as the default shell." (interactive) - (let ((list-buffer (current-buffer))) - (message "%s <==" (multishell-name-from-entry (tabulated-list-get-id))) - (multishell-pop-to-shell '(16) (tabulated-list-get-id)) + (let ((list-buffer (current-buffer)) + (entry (tabulated-list-get-id))) + (message "%s <==" (multishell-name-from-entry entry)) + (multishell-pop-to-shell '(16) entry) (with-current-buffer list-buffer - (revert-buffer)))) -(defun multishell-list-open-here () - "Switch to current entry's shell buffer." - (interactive) - (let ((list-buffer (current-buffer))) - (multishell-pop-to-shell nil (tabulated-list-get-id) 'here) + (revert-buffer) + (multishell-list-goto-item-by-entry entry)))) + +(defun multishell-list-open-here (&optional arg) + "Switch to current entry's shell buffer. + +The shell is started if it's not already going, unless this is +invoked with optional `universal-argument'. In that case we +switch to the buffer but don't activate (or deactivate) it it." + (interactive "P") + (let* ((list-buffer (current-buffer)) + (entry (tabulated-list-get-id))) + (if arg + (switch-to-buffer + (multishell-bracket (multishell-name-from-entry entry))) + (multishell-pop-to-shell nil entry 'here)) (with-current-buffer list-buffer - ;; In case they use switch-to-buffer or whatever to return. (revert-buffer)))) -(defun multishell-list-delete () - "Remove current shell entry, and prompt for buffer-removal if present. - -\(We depend on intrinsic confirmation prompts for active buffers, -supplemented by our own when buffer is inactive.)" - (interactive) +(defun multishell-list-delete (&optional arg) + "Remove current shell entry, and prompt for buffer-removal if present." + (interactive "P") (let* ((entry (tabulated-list-get-id)) (name (multishell-name-from-entry entry)) (name-bracketed (multishell-bracket name)) @@ -54,10 +80,16 @@ supplemented by our own when buffer is inactive.)" (kill-buffer name-bracketed))) (tabulated-list-delete-entry))) -(defun multishell-list-edit-entry () - "Edit the value of current shell entry." - (interactive) - (let* ((where (save-excursion (beginning-of-line) (point))) +(defun multishell-list-edit-entry (&optional arg) + "Edit the value of current shell entry. + +Submitting the change will not launch the entry, unless this is +invoked with optional `universal-argument'. In the latter case, +submitting the entry will pop to the shell in a new window, +starting it if it's not already going." + + (interactive "P") + (let* ((list-buffer (current-buffer)) (entry (tabulated-list-get-id)) (name (multishell-name-from-entry entry)) (revised (multishell-read-unbracketed-entry @@ -71,18 +103,20 @@ supplemented by our own when buffer is inactive.)" (when (and (not (string= name revised-name)) (setq buffer (get-buffer (multishell-bracket name)))) (with-current-buffer buffer - (rename-buffer (multishell-bracket revised-name)))) + (rename-buffer (multishell-bracket revised-name))))) + (when arg + (multishell-pop-to-shell nil revised-name)) + (with-current-buffer list-buffer (revert-buffer) - (goto-char where)))) + (multishell-list-goto-item-by-entry revised)))) (defun multishell-list-clone-entry (&optional arg) - "Create a new list entry based on editing the current one. + "Create a new list entry, edited from the current one, ready to launch. -You will be left in the list at the entry, not yet launched. +If you provide an optional `universal-argument', the new entry +will be launched when it's created. -Providing a universal argument will also open the new shell. - -The already existing current entry is left untouched." +The already existing original entry is left untouched." (interactive "P") (let* ((prototype (tabulated-list-get-id)) (name (multishell-name-from-entry prototype)) @@ -95,19 +129,18 @@ The already existing current entry is left untouched." (when (not (string= new prototype)) (multishell-register-name-to-path new-name new-path) (revert-buffer) - (goto-char (point-min)) - (re-search-forward (format "^ . \\b%s\\b" - (regexp-quote new-name))) - (beginning-of-line)))) + (multishell-list-goto-item-by-entry new) + (when arg + (multishell-pop-to-shell nil new-name))))) (defun multishell-list-placeholder (value default) "Return VALUE if non-empty string, else DEFAULT." (if (or (not value) (string= value "")) default value)) -(defconst multishell-list-active-buffer-flag "+") -(defconst multishell-list-inactive-buffer-flag ".") -(defconst multishell-list-absent-buffer-flag "x") +(defconst multishell-list-active-flag "+") +(defconst multishell-list-inactive-flag ".") +(defconst multishell-list-absent-flag "x") (defun multishell-list-entries () "Generate multishell name/path-spec entries list for tabulated-list." @@ -120,10 +153,10 @@ The already existing current entry is left untouched." (get-buffer (multishell-bracket name)))) (status (cond ((not buffer) - multishell-list-absent-buffer-flag) + multishell-list-absent-flag) ((comint-check-proc buffer) - multishell-list-active-buffer-flag) - (t multishell-list-inactive-buffer-flag))) + multishell-list-active-flag) + (t multishell-list-inactive-flag))) (rest (cadr splat)) (dir (or (file-remote-p rest 'localname) rest)) @@ -135,18 +168,38 @@ The already existing current entry is left untouched." (list entry (vector (format "%d" recency) status - name + (multishell-list--decorate-name name) (multishell-list-placeholder hops "-") (multishell-list-placeholder dir "~"))))) (multishell-all-entries)))) -(defun compare-strings-as-numbers (a b) +(defun multishell-list-goto-item-by-entry (entry) + "Position at beginning of line of tabulated list item for ENTRY." + (goto-char (point-min)) + (while (and (not (eobp)) + (not (string= (tabulated-list-get-id) entry))) + (forward-line 1))) + +(defun multishell-collate-row-strings-as-numbers (a b) (let ((a (aref (cadr a) 0)) (b (aref (cadr b) 0))) (> (string-to-number a) (string-to-number b)))) +(defun multishell-list--decorate-name (name) + (propertize name + 'font-lock-face 'multishell-list-name + 'mouse-face 'highlight)) + +(defun multishell-list-mouse-select (event) + "Select the shell whose line is clicked." + (interactive "e") + (select-window (posn-window (event-end event))) + (let ((entry (tabulated-list-get-id (posn-point (event-end event))))) + (multishell-pop-to-shell nil entry 'here))) + (defvar multishell-list-mode-map (let ((map (make-sparse-keymap))) + (set-keymap-parent map tabulated-list-mode-map) (define-key map (kbd "c") 'multishell-list-clone-entry) (define-key map (kbd "d") 'multishell-list-delete) (define-key map (kbd "\C-k") 'multishell-list-delete) @@ -156,14 +209,23 @@ The already existing current entry is left untouched." (define-key map (kbd " ") 'multishell-list-open-pop) (define-key map (kbd "O") 'multishell-list-open-as-default) (define-key map (kbd "RET") 'multishell-list-open-here) + (define-key map [mouse-2] 'multishell-list-mouse-select) + (define-key map [follow-link] 'mouse-face) map)) (define-derived-mode multishell-list-mode tabulated-list-mode "Shells" "Major mode for listing current and historically registered shells. + +Initial sort is from most to least recently used: + +- First active shells, flagged with '+' a plus sign +- Then, inactive shells, flagged with '.' a period +- Then historical shells that currently have no buffer, flagged with 'x' an ex + \\{multishell-list-mode-map\}" (setq tabulated-list-format [;; (name width sort '(:right-align nil :pad-right nil)) - ("#" 0 compare-strings-as-numbers :pad-right 1) + ("#" 0 multishell-collate-row-strings-as-numbers :pad-right 1) ("! " 1 t :pad-right 1) ("Name" 15 t) ("Hops" 30 t) @@ -182,10 +244,15 @@ You can get to this shell listing manager by recursively invoking \\[multishell-pop-to-shell] at either of the `multishell-pop-to-shell' universal argument prompts." (interactive) - (let ((buffer (get-buffer-create "*Shells*"))) + (let ((from-entry (car (multishell-history-entries + (multishell-unbracket (buffer-name + (current-buffer)))))) + (buffer (get-buffer-create "*Shells*"))) (pop-to-buffer buffer) (multishell-list-mode) - (tabulated-list-print))) + (tabulated-list-print) + (when from-entry + (multishell-list-goto-item-by-entry from-entry)))) (provide 'multishell-list) (require 'multishell) diff --git a/packages/multishell/multishell.el b/packages/multishell/multishell.el index f746c57..8d19a05 100644 --- a/packages/multishell/multishell.el +++ b/packages/multishell/multishell.el @@ -3,7 +3,7 @@ ;; Copyright (C) 1999-2016 Free Software Foundation, Inc. ;; Author: Ken Manheimer <ken.manhei...@gmail.com> -;; Version: 1.1.2 +;; Version: 1.1.3 ;; Created: 1999 -- first public availability ;; Keywords: processes ;; URL: https://github.com/kenmanheimer/EmacsMultishell @@ -59,6 +59,13 @@ ;; ;; Change Log: ;; +;; * 2016-02-09 1.1.3 Ken Manheimer: +;; multishell-list: +;; - add some handy operations, like cloning new entry from existing +;; - add optional behaviors to existing operations for returning to +;; stopped shells without restarting them. +;; - solidify maintaining focus on current entry +;; - fix miscellaneous. ;; * 2016-01-31 1.1.2 Ken Manheimer: ;; - Settle puzzling instability of multishell-all-entries ;; - The accumulations was putting items going from more to less active @@ -155,7 +162,7 @@ (require 'savehist) (require 'multishell-list) -(defvar multishell-version "1.1.2") +(defvar multishell-version "1.1.3") (defun multishell-version (&optional here) "Return string describing the loaded multishell version." (interactive "P") @@ -677,7 +684,8 @@ and path nil if none is resolved." (when (and path (not is-active)) (when (and (derived-mode-p 'shell-mode) (file-remote-p path)) - ;; Returning to disconnected remote shell - tidy up: + ;; Returning to disconnected remote shell - do some tidying. + ;; (Prevents the "Args out of range" failure when reconnecting.) (tramp-cleanup-connection (tramp-dissect-file-name default-directory 'noexpand) 'keep-debug 'keep-password))