A quick pop-up shell for emacs

Posted: October 12, 2011 in Emacs, Emacs Lisp
Tags:

Some file managers provide a shortcut to quickly embed some terminal that’s initialized with the current directory as cwd. Wouldn’t that be cool to have in emacs?

For example, you edit some file, and then you want to quickly commit it. Using the code below, you can do that like this:

  • Edit the file and save: C-x C-s
  • Popup a shell: F12
  • Check changes and commit (using svn):
    $ svn diff | colordiff
    $ svn ci -m "some changes" RET
  • Close the popup shell again: F12
  • Here’s the code:

    (defvar th-shell-popup-buffer nil)
    
    (defun th-shell-popup ()
      "Toggle a shell popup buffer with the current file's directory as cwd."
      (interactive)
      (unless (buffer-live-p th-shell-popup-buffer)
        (save-window-excursion (shell "*Popup Shell*"))
        (setq th-shell-popup-buffer (get-buffer "*Popup Shell*")))
      (let ((win (get-buffer-window th-shell-popup-buffer))
    	(dir (file-name-directory (or (buffer-file-name)
    				      ;; dired
    				      dired-directory
    				      ;; use HOME
    				      "~/"))))
        (if win
    	(quit-window nil win)
          (pop-to-buffer th-shell-popup-buffer nil t)
          (comint-send-string nil (concat "cd " dir "\n")))))
    
    (global-set-key (kbd "<f12>") 'th-shell-popup)

    Using that, hitting F12 will popup a *Popup Shell* buffer, initialized in the directory containing the file your are currently editing. If the current buffer is not associated with a file, then the shell’s cwd is your HOME directory.

    Hitting F12 while the popup shell buffer is visible will hide it again. So you can use it as a simple toggle.

    The code ensures that the same shell buffer is reused over and over again (unless you kill it), so that you don’t end up with hundredth of them.

    And here’s a screenshot:

    UPDATES:

    • 2011-10-13 08:03: Made it work for dired buffers.
    • 2012-02-24 14:41: Use quit-window instead of delete-window.
    Comments
    1. rodrigo says:

      Have you tried multi-term.el? It seems to do exactly what you describe.

      http://www.emacswiki.org/emacs/MultiTerm

      use it with (multi-term-dedicated-toggle)

      • Tassilo Horn says:

        Indeed, multi-term-dedicated-toggle looks pretty similar. The big difference is that my simple version uses shell instead of term, which I like better, because it’s more light-weight.

        In general, I prefer a usual terminal emulator running zsh for real shell work, because of its awesome completion capabilities. For example, typing java -cp foo.jar:bar.jar <TAB> will complete based on the compiled class files contained in either of the jars, or scp horn@foo.bar.com:/home/horn/<TAB> also completes on remote hosts.

        This popup shell in emacs is more for quick commands you frequently want to issue without opening a terminal, e.g., version control commands. So I’ll stick to my home-brewn version instead of adding yet another extension to my load path that offers so much functionally beyond what I need that I won’t use anyway. :-)

    2. David says:

      I don’t understand why you don’t use emacs’ vc integration (i.e. C-x v v)

      • Tassilo Horn says:

        Because that would commit only the current file. Ok, you can use vc-dir, mark all files you want to commit in one changeset, and then do it. But IMHO, I can do that more quickly in a shell.

        The good thing about emacs’ vc integration is that it provides a unified interface for all relevant version control systems. But that’s exactly what I don’t like. I want to know and use the exact commands of each system I work with (svn, cvs, hg, git, bzr). In case something goes wrong, at least you can get qualified help when you can explain exactly what you did.

        After working several years with different version control systems, now my shell history knows any command that I will ever need anyway. :-)

    3. Phil says:

      No doubt useful, although for this kind of one-liner I almost always find M-! sufficient. It likewise executes in the cwd for the current buffer, shows me the output (in the minibuffer if sufficiently small), has a command history, and only requires any kind of ‘hiding’ afterwards if there was enough output to make a *Shell Command Output* buffer (in which case I can just use winner-undo to make it go away).

      • Tassilo Horn says:

        Agreed, so my example should be made of at least two commands: svn diff | colordiff for checking what I changed with some colors (colordiff produces ascii escape sequences that won’t work with M-!), and then svn ci -m "bla bla". :-)

    4. Peter Mielke says:

      I’ve been using multiple shells for over 10 years; see http://www.emacswiki.org/emacs/UsingMultipleShells This incorporates shell history for each directory. This way i can record what i did in each location (useful especially after a long time between sessions).

      I now record the history in one location (instead of the cwd) to get around directories where i don’t have write access.

    Leave a comment