开发者

How to open multiple terminals?

开发者 https://www.devze.com 2023-01-20 17:13 出处:网络
In Emacs, I often find myself in a situation where I need to jump back and forth between various source files to various terminals. However, I feel like I do not have a good way to do this efficiently

In Emacs, I often find myself in a situation where I need to jump back and forth between various source files to various terminals. However, I feel like I do not have a good way to do this efficiently and it's clumsy that you can only open one shell in Emacs (shell, eshell, or term).

Moreover, I need an efficient way of juggle bet开发者_如何学Goween multiple terminals and source files.

How can I achieve this?


You can have as many terminals and shells open at once as you want. Just use M-x rename-buffer to change the name of an existing *term* or *shell* buffer, and the next time you do M-x term or M-x shell, a brand new buffer will be created. In the case of M-x shell, a prefix argument will cause you to be prompted for the name of the new shell buffer, as offby1 noted.

A few years ago I had a job where I had to regularly log in to various production servers named "host01.foo.com", "host02.foo.com", etc. I wrote a little function like this one to make it easier to manage them all:

(defun ssh-to-host (num)
  (interactive "P")
  (let* ((buffer-name (format "*host%02d*" num))
         (buffer (get-buffer buffer-name)))
    (if buffer
        (switch-to-buffer buffer)
      (term "/bin/bash")
      (term-send-string
       (get-buffer-process (rename-buffer buffer-name))
       (format "ssh host%02d.foo.com\r" num)))))

Then I bound this command to (say) s-h (super H), enabling me to just type M-5 s-h. If I didn't already have a buffer named *host05*, it would start a new terminal emulator buffer, rename it to *host05*, and ssh me into host05.foo.com. If buffer *host05* already existed, it would simply switch me to it. Quite handy!


You can certainly have multiple interactive shells open. Try typing C-u M-x shell RET RET.


Try using MultiTerm to open multiple shells.


You can use Emacs Lisp Screen, which emulates GNU Screen and provides easy key bindings to jump to and between a number of different shells.


I use many methods for incorporating my terminal life into Emacs:

  • elscreen.el is a life saver, if you have a complicated window layout like gdb or have simply become overwhelmed with clutter you just open a new screen. In your case you could dedicate one screen to terminals.
  • multi-term.el makes managing terminals a bit easier.
  • shell-pop.el, a great tool for quick terminal access. shell-pop lets you assign a key to opening and closing a specific shell buffer window, if you've used drop-down terminals like tilda you know how incredibly handy this can be:

Here's and example of my shell-pop configuration, I use the key C-t to pop up an eshell:

(require 'shell-pop)
(shell-pop-set-internal-mode "eshell")     ; Or "ansi-term" if you prefer
(shell-pop-set-window-height 60)           ; Give shell buffer 60% of window
;; If you use "ansi-term" and want to use C-t
;; (defvar ansi-term-after-hook nil)
;; (add-hook 'ansi-term-after-hook
;;           '(lambda ()
;;              (define-key term-raw-map (kbd "C-t") 'shell-pop)))
;; (defadvice ansi-term (after ansi-term-after-advice (org))
;;  (run-hooks 'ansi-term-after-hook))
;; (ad-activate 'ansi-term)
(global-set-key (kbd "C-t") 'shell-pop)


I usually do an an M-x server-start and then use emacsclient --no-wait to open files. I've aliased that to e with some embellishments so that it's a little more convenient.

I do all my work in a single terminal and just "throw" the files I want to edit into Emacs using e. Inside Emacs, I juggle around using iswitchb and it works just fine. YMMV.


I regularly used 10 or so shells in my old workplace. The secret is you have to rename additional shell buffers. I did this automatically though in my .emacs, creating and naming the shells logically (I had projnameRun and projnameBuild for every project). Worked really well together with anything, making it very easy to refind the right shell (you use the end of the project name combined with either r or b for run/build).


Instead of having several terminal windows in emacs, I spawn a different xterm whenever I need a new terminal. This of course is bearable because I use a very lightweight terminal emulator (urxvt) which starts in under 0.2s.
Then I use my window manager to switch between them and emacs frames. A configurable window manager will have plenty of options to tune to switch between windows (extremely) efficiently. Inside emacs, I use windmove and ido-mode, and have bound to C-tab a function that switches to the last buffer (because I use C-x b in that fashion a lot).

So um, not sure how useful it is to you since it's quite different from your use pattern, but this is what works for me.


I had exactly the same problem some years ago, and found nothing that satisfied me; so I wrote my own "toggle shell" function. It toggles between the current frame or window configuration and a system shell buffer. It can also put the shell into a dedicated frame, and inject a pushd to the current buffer directory.

This is an excerpt from my .emacs:

(defvar --toggle-shell-last-window-conf nil "The last window configuration.")
(defvar --toggle-shell-last-buf nil "The last buffer object in case there's no last window configuration.")
(defvar --toggle-shell-last-frame nil "The frame that was selected when opening a shell buffer.")

(defun --toggle-shell-have-conf ()
  (window-configuration-p --toggle-shell-last-window-conf))

(defun --toggle-shell-store-last-conf ()
  (setq --toggle-shell-last-buf (current-buffer)
    --toggle-shell-last-frame (selected-frame)
    --toggle-shell-last-window-conf (current-window-configuration)))

(defun --toggle-shell-restore-last-conf ()
  (if (--toggle-shell-have-conf)
      (progn (raise-frame --toggle-shell-last-frame)
         (set-window-configuration --toggle-shell-last-window-conf))
    (let ((bufnam (if (bufferp --toggle-shell-last-buf)
              (buffer-name --toggle-shell-last-buf) --toggle-shell-last-buf)))
      (if bufnam
      (if (get-buffer bufnam) (switch-to-buffer bufnam t)
        (message "%s: buffer not available" bufnam))))))

(defun --toggle-shell (&optional display inject-cd)
  "Toggles between current buffers and a system shell buffer. With prefix-arg
close the shell.

When DISPLAY is 'vertical splits the shell as vertical window; when 'frame uses
a dedicated frame (default: single window). When INJECT-CD executes a `pushd'
to the working directory of the buffer from which you toggled the shell."
  (interactive)
  (let* ((shell-buf (get-buffer "*shell*"))
     (shell-window          ; non-nil when currently displayed
      (if shell-buf (get-buffer-window shell-buf t)))
     (shell-frame
      (if shell-window (window-frame shell-window)))
     (in-shell (eq (current-buffer) shell-buf))
     (vertical (string= display 'vertical))
     (popup-frame (or (string= display 'frame)
              (and inject-cd (not (bufferp shell-buf)))
              (and (framep shell-frame)
                   (not (eq shell-frame (selected-frame)))))))
    ;; With prefix-arg close shell, restore windows. Otherwise (no prefix-arg)
    ;; toggle shell window; restore windows when called twice in a row, or the
    ;; current buffer is the shell buffer (`in-shell').
    (if current-prefix-arg
    (if (bufferp shell-buf)
        (progn (message "Exiting shell '%s'" (buffer-name shell-buf))
           (kill-buffer shell-buf)
           (if in-shell (--toggle-shell-restore-last-conf)))
      (error "No shell buffer to kill."))
      ;; If already in shell-buffer toggle back to stored frame-configuration.
      (if (and in-shell (not inject-cd))
      (progn
        (--toggle-shell-restore-last-conf)
        ;; Recurse to reopen the shell-buffer in a dedicated frame, or
        ;; close the dedicated frame and reopen the buffer in a window.
        (if (and popup-frame (eq shell-frame (selected-frame)))
        (--toggle-shell 'frame inject-cd)
          (when (and popup-frame shell-frame)
        (delete-frame shell-frame)
        (--toggle-shell nil inject-cd))))
    ;; Not in shell buffer. Warp to it or create new one.
    (unless in-shell
      (--toggle-shell-store-last-conf))
    (if popup-frame
        (progn (switch-to-buffer-other-frame (or shell-buf "*shell*"))
           (raise-frame
            (or shell-frame (window-frame (get-buffer-window "*shell*" t)))))
      (if (> (count-windows) 1)
          (delete-other-windows)))
    ;; Finally `cd' into the working directory the current buffer.
    (let ((new-shell (not (bufferp shell-buf)))
          (new-dir       ; `default-directory' of `--toggle-shell-last-buf'
           (if --toggle-shell-last-buf
           (buffer-local-value 'default-directory --toggle-shell-last-buf))))
      ;; Open shell, move point to end-of-buffer. The new shell-buffer's
      ;; `default-directory' will be that of the buffer the shell was
      ;; launched from.
      (when vertical
        (if (> (count-windows) 1)
        (delete-other-windows))
        (split-window-vertically) (other-window 1))
      (funcall 'shell)
      (when new-shell
        (message "New shell %s (%s)" (buffer-name (current-buffer)) new-dir)
        (if inject-cd (sit-for 2))) ; wait for prompt
      (goto-char (point-max))
      ;; If on a command-prompt insert and launch a "cd" command (assume no
      ;; job is running).
      (when (and inject-cd new-dir)
        (save-excursion
          (backward-line-nomark) (end-of-line)
          (unless (setq inject-cd (re-search-forward comint-prompt-regexp (point-max) t))
        (error "Cannot `pushd', shell is busy")))
        (when (and inject-cd)
          (let* ((cmd (format
               "pushd '%s' %s" (comint-quote-filename new-dir)
               (if (buffer-file-name --toggle-shell-last-buf)
                   (format "# '%s'" (file-name-directory (buffer-file-name --toggle-shell-last-buf)))
                 ""))))
        ;; `shell-process-cd' set new `default-directory' and set
        ;; `shell-last-dir' to old. (If the pushd command is
        ;; successful, a dirs is performed as well; >nul discards this
        ;; output.)
        (shell-process-cd new-dir)
        (insert cmd)
        (comint-send-input)
        (message "%s: cd '%s'" (buffer-name --toggle-shell-last-buf) new-dir))
          )
        )
      )
    )
      )
    )
  )

--toggle-shell is the function that does the trick. I bind it to F12:

;;  F12         toggle between shell buffer and current window configuration
;;  SHIFT-F12 like before, but let shell buffer appear in a dedicated frame
;;  ALT-F12     inject a pushd to change to directory of current buffer
;;  CTRL-F12  `shell-command'

(global-set-key [(f12)] '--toggle-shell)
(global-set-key [(shift f12)] '(lambda()(interactive)(--toggle-shell 'frame)))
(global-set-key [(meta f12)] '(lambda()(interactive)(--toggle-shell nil t)))
(global-set-key [(meta f10)] '(lambda()(interactive)(--toggle-shell nil t)))
(global-set-key [(control f12)] 'shell-command) ; alias M-!

This is a significant bunch of code to be posted here. But it shall work well.


Semi related - you can quickly run a shell command on selected file with

M+shift+!

It saves a lot of time for smaller commands chmod etc


And maybe my quick pop-up shell also might help you. A quick pop-up shell for emacs


Ecb + eshell will be what you want exactly!


I use vi, but hope this helps. I can open as many terminals as I want by (eg. in Ubuntu 16.04):

ctrl + alt + t

I usually open 2 terminals, and move (position) one terminal to the right by:

ctrl + super + right-arrow

and move the other terminal to the left by:

ctrl + super + left-arrow

so that I have a divided screen by 2 terminals.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号