project-x
project-x copied to clipboard
Ehancements to Emacs' built in project library.
#+title: Project-X #+author: Karthik Chikmagalur #+STARTUP: nofold
- /New in v 0.1.6:/ =project-x-local-identifier= can now be a list of file names
- /New in v 0.1.5:/ Autosave the state of the active project periodically.
Project-X adds a couple of convenience features for Emacs' =project.el= library.
-
Recognize any directory with a =.project= file as a project (name customizable). Also works if any parent directory has this file.
-
Save and restore project files and window configurations across sessions. Project-X will load all /saved project/ files and directories (as =dired= buffers) and try to recreate the window configuration at the time of saving. Project-X can also auto-save your current project's window configuration regularly.
-
Enhanced project switching: When you switch projects you switch window configurations too, (re)opening files as needed. #+ATTR_ORG: :width 500 #+ATTR_HTML: :width 1000px [[file:project-switching.gif]]
-
Restoring project state in a new Emacs instance. Note the new option ("Restore windows") in the project dispatch menu: #+ATTR_ORG: :width 500 #+ATTR_HTML: :width 1000px [[file:project-switching-2.gif]]
-
While Emacs has many built-in features to save and restore state (bookmarks, desktop, window-configurations and more) none of them allow you to bookmark and switch to, across Emacs sessions, a collection of files, buffers and windows together as a unit... or project. Hence project-x.
More features are planned, but note that Emacs' project library is a young project. As it is developed, some or all of the features in project-x might become obsolete. (In fact, that would be great.)
** Setup and customization Load =project-x.el=, then run =(project-x-mode +1)=.
OR, with =use-package=: #+begin_src emacs-lisp (use-package project-x :load-path "~/path/to/project-x/" :after project :config (setq project-x-save-interval 600) ;Save project state every 10 min (project-x-mode 1)) #+end_src
OR, if you do not want to use the (opinionated) minor-mode =project-x-mode=,
#+begin_src emacs-lisp (use-package project-x :load-path "~/path/to/project-x/" :after project :config (add-hook 'project-find-functions 'project-x-try-local 90) (add-hook 'kill-emacs-hook 'project-x--window-state-write) (add-to-list 'project-switch-commands '(?j "Restore windows" project-x-windows) t) :bind (("C-x p w" . project-x-window-state-save) ("C-x p j" . project-x-window-state-load))) #+end_src
OR, to automatically reload the windows when switching to a project,
#+begin_src emacs-lisp (use-package project-x :load-path "~/path/to/project-x/" :after project :config (add-hook 'project-find-functions 'project-x-try-local 90) (add-hook 'kill-emacs-hook 'project-x--window-state-write) (setq project-switch-commands #'project-x-windows) :bind (("C-x p w" . project-x-window-state-save) ("C-x p j" . project-x-window-state-load)))) #+end_src
There are three customization options right now:
- =project-x-window-list-file=: File to store project window configurations. Defaults to your emacs config directory.
- =project-x-local-identifier=: String matched against file names to decide if a directory (or some parent thereof) is a project. Defaults to =.project=. You can also supply a list of strings instead. For example, node projects use a file named =package.json= to denote the root of a project, Elixir uses =mix.exs= and Julia uses =Project.toml=. You can use project-x to identify all of the above as project directories by setting #+BEGIN_SRC emacs-lisp (setq project-x-local-identifier '("package.json" "mix.exs" "Project.toml" ".project")) #+END_SRC
- =project-x-save-interval=: Number of seconds between autosaves of the current project window configuration. Defaults to nil (autosave disabled). This requires =project-x-mode= to be turned on.
** Usage
*** Session management The =project-x-mode= minor-mode is provided for convenience. It enables these features:
| Keybinding | Command | Effect | |-------------+-----------------------------+-----------------------------------------| | =C-x p w= | =project-x-window-state-save= | Save your current project session | | =C-x p j= | =project-x-window-state-load= | Load session from a project | | =C-x p p j= | =project-x-windows= | Load session from project dispatch menu |
Save a project session with =C-x p w= and you should be able to load it any time across Emacs sessions.
You can go back to your previous window configuration with =winner-undo=.
*** 'Local' projects To recognize 'local' projects with a ".project" file somewhere in the path, turn on =project-x-mode= OR run #+begin_src emacs-lisp (add-hook 'project-find-functions 'project-x-try-local 90) #+end_src
All =project.el= features should work as expected.
** Limitations :PROPERTIES: :ID: c1326cad-5dd9-4789-8e5e-74f5b012b546 :END:
-
This is currently limited to storing only the current frame configuration.
-
The only state saved is your project files, project =dired= buffers and the current frame configuration. No minor modes, registers or special buffers (shells, help buffers etc) are recorded. For complete recall you can look into the Desktop library for Emacs.
-
If you use multiple Emacs instances the project states saved to disk can get overwritten.
-
/Implemented in v0.1.5/: +Your project state needs to be manually saved to be restored. I'm looking into auto-saving the state any time a project buffer is opened or window displayed, or when switching projects.+
** Alternatives *** How does this compare with... **** ...just using window-configurations? Package-X does use window configurations under the hood. However, it has a few advantages:
- Your project state remains persistent across sessions, and any files or dired buffers are reopened if necessary.
- Your project state is associated with the project instead of with registers or data structures from other packages.
**** ...Tabs/Workspaces/persp etc? If you think in terms of projects, you may find it more convenient to use =project-x= through the project dispatch menu (=C-x p p=) to continue working from where you left off. This is a helper library to define and handle projects, not an overarching modification to your Emacs usage pattern.
**** ...Burly? [[https://github.com/alphapapa/burly.el][Burly]] provides a more universal method to save and restore frames and window configurations as Emacs bookmarks (thus persistent across sessions) that is not limited to the project metaphor. If you are looking for this feature but in a more general Emacs context you might be better served by it.
**** ...the Desktop library? See [[id:c1326cad-5dd9-4789-8e5e-74f5b012b546][Limitations]]. Desktop restores your entire session, this is a much diminished version of the same for individual projects. But desktop being an all-or-nothing affair (without extensive customization) is also a disadvantage. Here each project gets its own desktop state.
**** ..Projectile? =project.el= is still very basic in its features and =project-x= is a small addition to it. However, as far as I know Projectile does not offer the ability to save and restore your project sessions (including window configurations).
** Planned features
- [X] Autosave the current project configuration when opening project files or switching projects.
- Save the window configuration across frames and tabs instead of only the current one.
** Technical notes Since this library uses the built in Emacs API to store the state, it is very compact. The machinery to maintain and recreate project states is only four short =defun='s. Likewise implementing a 'local' project backend is fewer than ten lines of code.
=project-x= creates entries containing project state information for a project in the data structure it uses (an associative list) only when you save its state. Thus it should remain fast even if you have thousands of projects so long as you actively work on a few at a time. If you experience slow down please raise an issue and I will consider reimplementing it as a hash table.