7

We all use multi-windows in Emacs, and for some projects, I developed a habit of using four windows, one each for a different buffer.

I wonder if I can define a function and when I start to work in a project, it will take the project name as input and set up the working environment like this (using an R project as an example):

  • Window 1 will always shows the main.R file in the project folder;
  • Window 2 will always shows the *R-main* process buffer;
  • Window 3 will shows the project_log.org file in the project folder;
  • Window 4 is a reference window, shows the dired buffer in the project folder.

Each window has equal size.

5
  • I'm an elisp beginner myself, but I'm sure this could be done relatively easily. Here's a relevant tutorial: ergoemacs.org/emacs/emacs_winner_mode.html. And this page lists some other functions that might be useful: chemie.fu-berlin.de/chemnet/use/info/elisp/elisp_26.html
    – Brian Z
    Mar 13, 2015 at 12:42
  • See suggestions in reddit.com/r/emacs/comments/1m73gs/… Mar 13, 2015 at 14:41
  • Good suggestions so far. You can use this package to set windows to "always" show specific buffers: emacs-purpose
    – nanny
    Mar 13, 2015 at 14:44
  • @yi.tang.uni I'm not familiar with R, but I think I can write a function that uses emacs-purpose to do what you want. Only, I'm not sure what you mean by "always". If you open another R file (e.g. "lib.R"), where should it open?
    – bmag
    Mar 17, 2015 at 16:33
  • @bmag I tried to use emacs-purpose but it seems really complex to me, I read the wiki pages but still don't have a clue. I don't think it is a good idea to lock the windows, but would like to give it go, so please lock the window 2 to R-main all the time if you can do. Mar 18, 2015 at 18:00

3 Answers 3

3

In addition to the other answers, consider bookmarks. If you use library Bookmark+ then you can bookmark any of these thingies to return to a particular configuration you like:

  • a desktop
  • a window configuration
  • a frame configuration

"Jumping" to such a bookmark restores the setup that was recorded. This means that you can easily and quickly switch among different desktops etc. You can of course jump to a bookmark programmatically (e.g. from your init file), as well as interactively.

The most general of these is a desktop bookmark. Starting with Emacs 24, it includes restoring frames, windows, and buffers as they were snapshot (as much as possible).

1

An easy way to do this is by storing a window configuration as a directory local variable.

First, arrange the window the way you like them, open files etc.

Add the window configuration as directory local variable:

(add-dir-local-variable nil 'my-window-conf (window-state-get nil t))

Restore the window configuration with

(defun my-window-conf-restore ()
  "Restore project window configuration."
  (interactive)
  (when (boundp 'my-window-conf)
    (delete-other-windows)
    (window-state-put my-window-conf)))
1

Using Purpose, you can try the following. Note that I don't really know R, so you need to fill in the code for creating the *R-main* buffer.

Also, if you open more than 2 files with the same name, then Emacs will start playing with their buffer names. To fix this, use a proper regexp in bmag-R-purpose-conf (exact regexp depends the value of uniquify-buffer-name-style).

;; define purposes suitable for R projects
(defvar bmag-R-purpose-conf (purpose-conf
                 "R-project"
                 ;; R files are considered "code" buffers
                 :regexp-purposes '(("\\.R$" . code))
                 ;; the *R-main* buffer is considered a "repl"
                 ;; buffer.
                 ;; the project_log.org buffer is considered a
                 ;; "log" buffer
                 :name-purposes '(("*R-main*" . repl)
                          ("project_log.org" . log))))

;; mysterious incantation - generated with `purpose-save-window-layout'
(defvar bmag-R-layout
  '(t
    (0 0 92 36)
    (nil
     (0 0 92 18)
     ;; top-left window is dedicated to "code" buffers (R files)
     (:purpose code :purpose-dedicated t :width 0.5 :height 0.5 :edges
           (0.0 0.0 0.5 0.5))
     ;; top-right window is dedicated to "repl" buffers (*R-main*)
     (:purpose repl :purpose-dedicated t :width 0.5 :height 0.5 :edges
           (0.5 0.0 1.0 0.5)))
    (nil
     (0 18 92 36)
     ;; bottom-left window is not dedicated, but will initialy show a "log"
     ;; buffer (project_log.org)
     (:purpose log :purpose-dedicated nil :width 0.5 :height 0.5 :edges
           (0.0 0.5 0.5 1.0))
     ;; bottom-right window is not dedicated, but will initialy show a dired
     ;; buffer
     (:purpose dired :purpose-dedicated nil :width 0.5 :height 0.5 :edges
           (0.5 0.5 1.0 1.0)))))

(defun bmag-project-root ()
  "Get project's root directory."
  (expand-file-name (read-directory-name "Project root directory: " nil nil t)))

(defun bmag-file-in-project (root name)
  (file-truename (concat root name)))

(defun bmag-open-R-project ()
  (interactive)
  (let ((root (bmag-project-root)))
    (purpose-mode)
    ;; activate Purpose configuration for current project
    (purpose-set-extension-configuration :project bmag-R-purpose-conf)
    ;; make current frame display windows as specified in `bmag-R-layout'
    (purpose-set-window-layout bmag-R-layout)
    ;; open relevant buffers. Purpose takes care of displaying them in the correct windows
    (dired root)
    (find-file (bmag-file-in-project root "/project_log.org"))
    (find-file (bmag-file-in-project root "/main.R"))
    ;; --- insert code for creating *R-main* here ---
    ))

You can adapt this solution to other languages. For example, Python:

(defvar bmag-python-purpose-conf
  (purpose-conf "python-project"
        :mode-purposes '((python-mode . code)
                 (inferior-python-mode . repl))
        :name-purposes '(("project_log.org" . log))))

;; note: `bmag-python-layout' is identical to `bmag-R-layout'
(defvar bmag-python-layout
  '(t
    (0 0 92 36)
    (nil
     (0 0 92 18)
     (:purpose code :purpose-dedicated t :width 0.5 :height 0.5 :edges
           (0.0 0.0 0.5 0.5))
     (:purpose repl :purpose-dedicated t :width 0.5 :height 0.5 :edges
           (0.5 0.0 1.0 0.5)))
    (nil
     (0 18 92 36)
     (:purpose log :purpose-dedicated nil :width 0.5 :height 0.5 :edges
           (0.0 0.5 0.5 1.0))
     (:purpose dired :purpose-dedicated nil :width 0.5 :height 0.5 :edges
           (0.5 0.5 1.0 1.0)))))

(defun bmag-open-python-project ()
  (interactive)
  (let ((root (bmag-project-root)))
    (purpose-mode)
    (purpose-set-extension-configuration :project bmag-python-purpose-conf)
    (purpose-set-window-layout bmag-python-layout)
    (dired root)
    (find-file (bmag-file-in-project root "/project_log.org"))
    (find-file (bmag-file-in-project root "/main.py"))
    (python-shell-switch-to-shell)))

If you use Projectile (or an equivalent package), you could use it to get the project's root directory automatically.

1
  • It sounds really good, I will try it soon and let you know. Mar 24, 2015 at 15:39

Your Answer

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

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