(This is a followup and different from Emacs Fixed Layout )

High Level

My typical Emacs Working environment is:

| NeoTree    |  Main                    |  Rhs                     |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |
|            |                          |                          |

where Main = some *.clj *.cljs file I'm editing, and RHS = cide-repl or cljs repl or eshell


Sometimes, some emacs functions screw up this layout. Examples: looking up documentation, running cider-debug, showing exceptions, etc ...

What I want:

I would a way to bind some keys, say C-x 1, C-x 2, C-x 3 (yes, I know this is terrible choice, but not focus of question) wherE:

C-x 1 = hide all existing windows, fire up 3-pane setup, with Neotree + last edited clj/cljs file, and RHS = cider-repl C-x 2 = same as C-x 1, but cljs-repl in RHS C-x 3 = same as C-x 1, but eshell in RHS


The main feature I need in this is a function to say:

  1. hide all existing windows

  2. create this three pane setup

    How do I do that?


Edit: How is this different from original question:

Original question: how can I force emacs to use this 3 pane setup.

This question: emacs is going to screw up my 3 pane setup. How do I have a three-key recovery shortcut?

  • The example-fn in the following link demonstrates how to programmatically create a 3-way window setup -- you can adjust it as needed (e.g., split left, right, up, or down): emacs.stackexchange.com/a/26970/2287 The function to hide all existing windows, except the selected window, is delete-other-windows. I don't use cider/repl etc., so I cannot help you there. Keyboard shortcuts are a different question, and there are plenty of examples available by Googling. And as stated in your previous thread, set-window-dedicated-p is your friend.
    – lawlist
    Nov 17, 2016 at 17:42
  • It may behoove you to look into Saving Window Configurations in Registers: gnu.org/software/emacs/manual/html_node/emacs/… There are many libraries dedicated to this concept. A popular library that saves window layouts is elscreen, which provides for a tabbed visual aid to switch between them.
    – lawlist
    Nov 17, 2016 at 17:45
  • @lawlist: Just wanted to say thanks for all 3 of your detailed responses! (on both questions)
    – eav db
    Nov 17, 2016 at 20:59
  • winner-mode might be of interest too - gnu.org/software/emacs/manual/html_node/emacs/…
    – npostavs
    Nov 18, 2016 at 15:54

1 Answer 1


One possible solution entails creating a few core functions that generate or locate the desired buffers (e.g., *NeoTree*, *eshell*, *cider-repl*), but not display them -- i.e., they are all setup and ready to go, but remain in the background waiting to be used. In this example, I have created two new functions called eshell-get-buffer-create and neotree-get-buffer-create. The latter may need some more work (please let me know), but the former is pretty straight forward and should not need any modifications. I am unfamiliar with cider-repl, so am only using a *scratch* buffer for the center "Main" window. The original poster may wish to create a special function that generates or locates the cider-repl buffer without displaying it, and then replace (get-buffer-create "*scratch*") with the said new function (which should return the buffer at the tail end of the function). I am not using any window registers in this example -- instead, I merely delete all other windows and set-up a new 3-way view each time example-fn is called. A keyboard shortcut of f5 is used in this example, but the original poster is free to change it to something else.

(require 'cl)
(require 'eshell)

(defun eshell-get-buffer-create (&optional arg)
"Create an interactive Eshell buffer.  Return the Eshell buffer,
creating it if needed.  The buffer used for Eshell sessions is
determined by the value of `eshell-buffer-name'.  A numeric prefix
arg (as in `C-u 42 M-x eshell RET') switches to the session with
that number, creating it if necessary.  A nonnumeric prefix arg
means to createa new session.  Returns the buffer selected (or created)."
  (interactive "P")
  (cl-assert eshell-buffer-name)
  (let ((buf (cond ((numberp arg)
        (get-buffer-create (format "%s<%d>"
        (generate-new-buffer eshell-buffer-name))
        (get-buffer-create eshell-buffer-name)))))
    (cl-assert (and buf (buffer-live-p buf)))
    (with-current-buffer buf
      (unless (derived-mode-p 'eshell-mode)

(require 'neotree)

(defun neotree-get-buffer-create (&optional dir)
"Return the neotree buffer -- creating it if needed.
The root directory may be set/reset using the optional
argument of DIR; else if buffer exists already use it
as-is; else use `neo-path--get-working-dir'."
  (let ((valid-start-node-p nil)
        (buffer (neo-global--get-buffer t)))
      (setf valid-start-node-p (neo-buffer--valid-start-node-p))
      (when (or dir (not valid-start-node-p))
        (let ((path (or dir (neo-path--get-working-dir)))
          (unless (and (file-exists-p path)
                       (file-directory-p path))
            (throw 'error "The path is not a valid directory."))
          (setq start-path (expand-file-name (substitute-in-file-name path)))
          (setq neo-buffer--start-node start-path)
          (cd start-path)
          (neo-buffer--save-cursor-pos path nil)
          (let ((start-node neo-buffer--start-node))
            (unless start-node
              (setq start-node default-directory))
             ;; starting refresh
             (setq neo-buffer--start-line neo-header-height)
             (neo-buffer--insert-tree start-node 1)
             (goto-char (point-min)))))))

(defun example-fn ()
"To try this example, type:  M-x example-fn"
  (let ((a (neotree-get-buffer-create))
        (b (get-buffer-create "*scratch*"))
        (c (eshell-get-buffer-create)))
    (switch-to-buffer b) ;; Main -- center window.
    ;; See the doc-strings for `split-window' and `window--display-buffer'.
    (window--display-buffer a (split-window (selected-window) nil 'left)
      'window `((window-width . ,neo-window-width)) display-buffer-mark-dedicated)
    (window--display-buffer c (split-window (selected-window) nil 'right)
      'window nil display-buffer-mark-dedicated)))

(global-set-key [f5] 'example-fn)
  • 1
    Thanks for all your hardwork! Though new to elisp, I'm familiar with both Clojure and SICP -- so I'll make this work. Thanks for providing (1) the basic skeleton, (2) the functions I need to use, with examples of how they're called, and (3) the overall structure!
    – eav db
    Nov 18, 2016 at 5:09
  • 1
    You are very welcome. I enjoyed creating the noselect buffer generating/locating functions for neotree and eshell, and have added them to my own arsenal for future use. I personally keep display-buffer-mark-dedicated at the default value of nil, but you may wish to look up the doc-string for that variable and see if you want to set either one of those to t within the example-fn. I don't use neotree daily, but like to play around with tree-view libraries occasionally -- so that is why I said in the answer above that it may need some revisions (i.e., I'm not very familiar with it).
    – lawlist
    Nov 18, 2016 at 5:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.