0

Ever since I first started using Emacs (Spacemacs) a few months ago I've been trying to set up a sane multi-project workflow. I got the single project workflow going by having Emacs start up into what I had open last, all windows, all buffers and all cursor positions (using dotspacemacs-auto-resume-layouts t)

While that's fine for single projects, it doesn't scale at all.

What I'm after is similar to how Sublime Text handles projects, ideally with a .projectile file that keeps all the project's settings. That way a folder is portable, can be zipped up and passed on.

So far my setup is pretty poor with projectile. All it does is open a frame with a dired buffer and nothing else. Not all that useful. With Sublime Text I have to ship the project definition file with the rest of the folders. Usually that works by placing the project file in the parent folder along with a readme file and zip it from there.

Since Emacs is a wee bit older than Sublime, I was hoping there would be a simpler way (once it's set up) that makes this whole thing easier.

The thing is, I just don't know where to begin there. It probably sounds quite broad so I'll just list the specifics.

  • Have a dotfile in a folder that stores:
    • Frame layout (Window position & size)
    • Buffer content for each window

The main issue here is portability. Bookmark+ like mentioned before seems to only deal in absolute paths. For portability that creates problems, especially since we have projectile-project-root available to get the project path. So a relative solution must be possible somehow.

Basically everything that the auto resume does, but for each project. In essence I want to launch Emacs and just see the welcome screen, pick a project and it comes up like the last time I've left it.

I'm using Emacs-plus (homebrew) 25/26 with Spacemacs on Desktop not Terminal (just in case that's relevant). My main modes are markdown-mode, org-mode, image-mode and later on probably latex-mode (haven't checked yet on LaTex modes for Emacs).

2
  • Directory local variables do some of what you want.
    – Qudit
    Jun 12, 2018 at 4:08
  • That just opened another can of worms :) Thanks for that. Not to derail this too much but I wonder how far this goes. Could I populate a file with something I fetch from a database / external file source for example when I open a file of a certain name? Jun 12, 2018 at 17:05

3 Answers 3

1

You like having Emacs start up into what I had open last, all windows, all buffers.... But while that's fine for single projects, it doesn't scale at all. In that case, consider using multiple desktop files.

Unfortunately, vanilla desktop.el does not facilitate using multiple desktop files -- and certainly not multiple such files kept in the same directory.

Bookmark+ can help here. You can just hit a key to bookmark (or update an existing bookmark) the current desktop. And you can later (in any Emacs session) hit another key to restore a bookmarked desktop.

You can have any number of such desktop bookmarks. You can filter the current listed bookmarks to show only desktop bookmarks, and you can cycle among desktop bookmarks (or any set of bookmarks), flipping from one desktop to the next.

You can also leverage other types of bookmarks to quickly switch among different projects (sets of bookmarks): Dired, Dired tree, bookmark-list, and bookmark-file bookmarks.

3
  • (1/2) — I've experimented with desktop.el in the beginning. I saved them in the respective project folders and on each project switch had to navigate anew to the project folder and tell Emacs "this is the folder where my desktop setup is stored". Also the desktop.el setup seems wholly unaware of projectile. It contained full paths to each file, not relative paths. So when I pass on a project, it immediately breaks for the next person, or for myself if used on another machine. Bit of a conundrum. Jun 12, 2018 at 17:23
  • (2/2) — I looked into Bookmark+ before but it seems "machine local" rather than "project local" for lack of a better term in the way it stores things. I'm looking for something that upon loading a project brings back the entire last state of that project so you hit the ground running each time instead of having to put Humpty together again each time. A bit like index.html, you navigate to a dir with it in the browser and it displays things according to what you specified in that file. Just with the project listing in Emacs. Pick a project, start working. Jun 12, 2018 at 17:24
  • You can put desktop files anywhere you like. With desktop bookmarks you need not ever "navigate anew to the project folder...". And you can use Dired bookmarks where the Dired buffer contains files from anywhere, including from different drives or even different machines. But yes, at some point an absolute file/dir name needs to be provided. You can use function bookmarks and sequence bookmarks to take care of arbitrary processing such as mapping the non-dir part of a file name to different directories. But yes, "project" can mean different things, and bookmarks are general building blocks.
    – Drew
    Jun 12, 2018 at 20:08
1

See https://github.com/technomancy/find-file-in-project (FFIP)

I just open recent file and Emacs should automatically switch the project by checking the full path of opened file.

Technology is already invented ages ago. It's called buffer local variable (https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer_002dLocal-Variables.html). FFIP takes full advantage of this technology. That's why I suggest it

Here is sample setup from its web site. As you required, a simple ~/.custom.el is enough to manage multiple projects.

;; If the full path of current file is under SUBPROJECT1 or SUBPROJECT2
;; OR if I'm reading my personal issue track document,
(defun my-setup-develop-environment ()
  (interactive)
  (when (ffip-current-full-filename-match-pattern-p "\\(PROJECT_DIR\\|issue-track.org\\)")
    ;; Though PROJECT_DIR is team's project, I care only its sub-directory "subproj1""
    (setq-local ffip-project-root "~/projs/PROJECT_DIR/subproj1")
    ;; well, I'm not interested in concatenated BIG js file or file in dist/
    (setq-local ffip-find-options "-not -size +64k -not -iwholename '*/dist/*'")
    ;; for this project, I'm only interested certain types of files
    (setq-local ffip-patterns '("*.html" "*.js" "*.css" "*.java" "*.xml" "*.js"))
    ;; maybe you want to search files in `bin' directory?
    (setq-local ffip-prune-patterns (delete "*/bin/*" ffip-prune-patterns))
    ;; exclude `dist/' directory
    (add-to-list 'ffip-prune-patterns "*/dist/*"))
  ;; insert more WHEN statements below this line for other projects
  )
;; most major modes inherit from prog-mode, so below line is enough
(add-hook 'prog-mode-hook 'my-setup-develop-environment)
0

After almost a year I revisited this problem and actually found something that's solving my problem in a fairly simple way. Two solutions actually. One is what I was after then and one solved what I needed now. Posting this in case someone else has a similar issue.

Approach 1 - Localising the Desktop file to the project

Adding this to my .spacemacs file in the dotspacemacs/user-init section did the trick:

(setq·desktop-path·'("."))

It sets the desktop location to the directory that's currently in use. In my case that's always the project directory. You can probably use the project path setting from projectile or a pwd variable too. I just like this one, it's fairly concise. I had these two set (desktop-save-mode 1) (setq desktop-save t) but ended up disabling them again for now. Emacs will ask me to save the desktop anyway when closing.

Approach 2 - Dired functions (keymapped)

To start projects I created a couple of functions to set up the rather specific environments I need, quickly. I tend to call these rather than relying on the desktop file as there are many empty buffers at the beginning that will trigger desktop file errors. Once all files have content (and aren't just empty, named buffers) I load the desktop file. Dired doesn't have that many function keys defined in spacemacs, so I might add a load desktop one there since projectile always opens in Dired mode anyway. Here are the pieces to roll your own.

Hook it into the mode

(with-eval-after-load 'dired
(define-key dired-mode-map [f3] #'write-window-setup))

That goes into my write-window-setup function where files are involved

(find-file "draft.org" t)

You can also use wildcards here like chapter_*.md or just chapter*.md if you want to suck in a whole book to your buffers. I have an almost identical setup in Vim now as well. The folder structure is currently set up with a small shell script so I can call it from either Emacs or Vim, or just from the shell if I have to create a few at once.

Having functions create the work environment is kind of neat. I always start with the same setup, all files are ready to write into and everything is in the same place. And once I have the files filled with content, it just loads them instead of empty buffers. No checking whether the file exists. It's either an empty buffer, ready to use or the contents of the file.

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.