Project-butler: A package to open buffers and arrange their windows when switching projects


Look ma, I made my first Emacs package! It’s called Project Butler. It adds a further option to the project-switch-commands of the built-in project.el and opens buffers and lays them out for you.

It uses a very simple syntax to describe window layouts. For example, 1|2_3< results in a layout like this:

  |    | 2  |
  | 1* |----|
  |    | 3  |

When set up for a project, all it takes is to switch to the project (e.g. by C-x p p ), choose the project, type “o”. And that’s it.

Optionally, the butler also cleans up after you.

See the git repo for details.

This is my first emacs package. Also I’m not a programmer by trade, I only wrote this to scratch my own itch. Any constructive feedback is very welcome!


This is an interesting idea, and I don’t want to devalue your work, but have you seen Activities? GitHub - alphapapa/activities.el: Activities for Emacs (suspend and resume activities, i.e. frames/tabs and their windows, buffers) It solves this problem in a more flexible, powerful way.

1 Like

Hey @alphapapa, thanks. No, in fact I don’t know Activities. When I started writing project-butler, I searched for other packages and I did find burly and bufler, but Activities apparently hadn’t been published, yet. On first glance, it looks like I wouldn’t have written my packages if Activities had existed, because they indeed seem to scratch the same itch. Activities seems to do much more, too. I’ll give it a closer look. Thanks for the heads-up!

1 Like

I gave Activities a whirl. A terrific package, as always, @alphapapa!
The only thing I’m missing is a project oriented workflow.
One could e.g. add an Activities option to project-switch-commands and provide an option to associate a defined activity to a project. So all it would take is to select a project and then hit e.g. “a” to resume the respective activity.
If activities had that, I could withdraw my PR for project-butler with MELPA. No need for two packages that are so similar in application.
What do you think? Would you be open to add such a workflow to Activities? I’d be happy to give it a go.

@jabbo I’m open to the idea of some kind of integration with the project library; feel free to open an issue on the tracker to discuss the details.

But note that this isn’t really necessary. If you set up an activity that shows buffers in a project, when you switch to that activity, you will have effectively switched to that project (because you’ll now be looking at that project’s files and buffers).

The only feature you seem to be wanting is to use project-switch-commands to switch to a project; but if you call activities-resume and switch to the activity with the same name as the project, what is the difference? It’s just a slightly different way to get the same result.

To put it another way, Activities “doesn’t care” about projects, only about buffers. But in the same way, project doesn’t care about buffers, only files. So if you use Activities to switch to buffers that are looking at a project’s files, project doesn’t know the difference.

Anyway, if you really want some kind of UI integration with project stuff, like I said, I’m open to the idea, but no one has cared enough to discuss the details yet, probably because there’s not much to actually do in that regard. activities and project are mostly orthogonal in their domains.

I haven’t yet found the time to try my hand at the code, but I have given it some thought in the back of my mind.

The difference only matters to people like me who leverage project.el for a lot of small other things. E.g. in my setup, when I call ripgrep from Emacs, it doesn’t use the current directory as its scope but asks project.el for the current project.

But that doesn’t change the fact that I could simply call activities-resume via hook after switching projects. We probably wouldn’t even need to touch the code of your package.

I have been thinking, however, about whether the use case these packages serve aren’t (slightly) different. For one, Activities does much more than the butler, and allows for a lot of flexible and different uses. It also provides a “pick-off-where-you-left-it” approach: save and resume. Project-butler declaratively defines a desired state with which to start out.

So on first glance, it’s the difference between returning to you desk as you have left it (Activities) or returning to a clean desk (project-butler). But of course, with activities-define and activities-revert you can return to a clean desk, too.

So the real difference lies in the… declarativeness declarativity declarative nature of defining what you want. Activities let’s you take a snapshot and make it default, project-butler lets you declare what you want via text and apply that configuration to different machines.
If I understand correctly, Activites stores the state in emacs lisp or alternatively (like burly) in the bookmarks, right?

Probably not, but I’m not opposed to making reasonable changes that might make such integration easier. As the docs and code say, I haven’t added all the possible hooks yet; I’m waiting to see what users need.

I’m also not opposed to having some kind of project integration, e.g. a default activity per-project, or perhaps even a set of activities per-project (though that might mean gating those activities through the project, something users would need to be aware of). I’d likely find that kind of feature useful, myself, but I haven’t felt a strong enough need to make it yet.

If you’re interested in these ideas, please do open discussions about them on the Activities repo and we can explore them concretely.

The state is persisted in a file using the persist library.

Burly stores state in Emacs bookmarks, as well as offering commands to write a saved state to a URL (which can be shared and opened on other systems).

Activities is based on Burly’s implementation; it would be trivial to export an activity’s state to a string or file that could be opened on other systems (keeping in mind that some commonalities would be necessary to work completely, e.g. file paths, major mode packages, etc, but that should be no different than with your package).