Project type support for Emacs builtin project.el
.
This Emacs package provides a Projectile like project management library atop Emacs
built-in project.el
. The end goal is to provide a stable and reliable out-of-the-box
project management experience for as many project types as possible while supporting
very targeted support for some project types like CMake.
Projectile is fantastic! The out of the box experience is amazing, but it’s also
quite overbearing. Currently projectile has almost 6000 lines of source code all
contained in a single file. This includes cross-platform facilities for detecting
the current project root, definitions for numerous project types, and a myriad of
helper functions (such as a pluggable TAGS table back-end, or wrappers across other
Emacs commands or packages with the sole amendment being they run in the project
root). Suffice it to say I think projectile is spread a little too thin and trying
to do way too much. This is a consequence of it evolving naturally from a time when
Emacs had poor built-in project support. Going forward I expect more packages to
start using the new built-in Emacs project.el
and Projectile becoming yet-another
pluggable backend for it. In that vein this project is simply trying to migrate
over the minimum feature set I find valuable from Projectile for my personal use.
Where possible I do also try to improve on features. For example Projectile doesn’t
support binding an interactive function as a compilation command (like
rustic-compile
) but Projection does.
Note: If any part of this description is no longer up-to-date please open a PR to discuss updating it :-).
- Clone the repo.
- Add the src and/or the src/projection-multi path to your Emacs
load-path
. - Load it when needed.
Projection and any extension packages are on MELPA. You can add this to your
package-archives
variable and then install through M-x package-install
.
(push '("melpa" . "https://melpa.org/packages/") package-archives)
(package-refresh-contents)
(package-install 'projection)
(package-install 'projection-multi)
Must of projection is optional. By default projection does not do anything, not even bind any keys (although it does provide a map to make this more convenient). Here’s a base configuration that will enable ALL the feature of projection out of the box. See Features for a list of features.
(use-package projection
:ensure t
;; Enable the `projection-hook' feature.
:hook (after-init . global-projection-hook-mode)
;; Require projections immediately after project.el.
:config
(with-eval-after-load 'project
(require 'projection))
:config
;; Uncomment if you want to disable prompts for compile commands customized in .dir-locals.el
;; (put 'projection-commands-configure-project 'safe-local-variable #'stringp)
;; (put 'projection-commands-build-project 'safe-local-variable #'stringp)
;; (put 'projection-commands-test-project 'safe-local-variable #'stringp)
;; (put 'projection-commands-run-project 'safe-local-variable #'stringp)
;; (put 'projection-commands-package-project 'safe-local-variable #'stringp)
;; (put 'projection-commands-install-project 'safe-local-variable #'stringp)
;; Access pre-configured projection commands from a keybinding of your choice.
;; Run `M-x describe-keymap projection-map` for a list of available commands.
:bind-keymap
("C-x P" . projection-map))
(use-package projection-multi
:ensure t
;; Allow interactively selecting available compilation targets from the current
;; project type.
:bind ( :map project-prefix-map
("RET" . projection-multi-compile)))
(use-package projection-multi-embark
:ensure t
:after embark
:after projection-multi
:demand t
;; Add the projection set-command bindings to `compile-multi-embark-command-map'.
:config (projection-multi-embark-setup-command-map))
This feature works just like projectiles projectile-compile-project
family of
commands. Each project can optionally expose one of the following command types
and you can use the associated projection-commands-TYPE-project
command to
invoke it interactively.
Type | Description |
---|---|
Configure | Run any pre-configure steps such as generating Makefiles. |
Build | Compile the project. |
Test | Run any configured tests for the current project. |
Run | Run the project (for example: Starting a game). |
Package | Produce a package from the built project. |
Install | Install the packaged project into an install directory. |
At any point you can customize or override what command to run for these
command-types by passing a prefix argument (C-u
) to the command. The command you
enter will be cached so subsequent attempts to run the same command-type will use
the same command. You can reset to the project defaults with M-x
projection-reset-project-cache
.
Note: Projection supports both shell-commands, interactive functions and helper functions which can return either of these as valid targets for each of these commands. This means, for example, we can support using rustic-modes builtin compilation commands and fallback to basic shell-commands when those aren’t defined. See projection-types for how this is configured.
Provides a more general purpose parallel to projectile-toggle-project-read-only
.
With this you can hook certain functions (Example: read-only-mode
) into a project
and retroactively apply it to both all the open buffers from that project and any
new buffers that will be opened in it.
Offers variants of projectile-ibuffer
and the ibuffer-projectile project in the
form of ibuffer-projection-current-project
and ibuffer-projection-set-filter-groups
.
The former creates and displays a dedicated ibuffer window for only buffers in the
current project. The latter pre-pends filters to group by a specific project for
all currently open projects.
Adds facilities for jumping to related files within a project. The most common use
case for this would be jumping between C++ header .h
and implementation .cpp
files.
This is already possible with Emacs’s builtin ff-find-other-file
command but
projection builds on top of it by supporting jumping to related files in other
directories or with alterations to the file-name beyond extensions. For example if
you have header files in an include directory and implementation files in a src
directory then projection-find-other-file
can still jump between them without any
extra configuration. If you’re working on a python project and define test files
with a test_BASENAME.py
format then projection-find-other-file
can also jump
between BASENAME.py
and test_BASENAME.py
. projection-find-other-file
is intended to
be a consistent and transitive command. You can invoke it repeatedly to cycle
between related files and the order in which you cycle will be consistent
independent of which file you’re currently in.
General associations between the current files extension and possible related file
extensions is configured in projection-find-other-file-suffix
. Supported suffixes
and prefixes for test files is configured by the project-type in
src/projection-types.el.
A variant of M-x recentf
for files exclusively in the current project.
compile-multi is a multi target interface to M-x compile
. It allows you to
configure and interactively select compilation targets based on arbitrary
projects.
Projection has an optional extension package called projection-multi-compile
to
integrate compile-multi
into the current project type. It can extract available
compilation targets from Makefiles, CMake configuration, etc. and let you execute
them easily. By default projection-multi-compile
determines all project types
matching the current project and then resolves compilation targets based on them.
For example a project that would match CMake and tox would let you select both tox
environments and CMake build targets.
Each target generation function in projection-multi also supports being run
independently. To select a tox task you can run M-x projection-multi-compile-tox
,
and you won’t be presented with CMake or any other target types. This bypasses
project type matching altogether and so may present targets not normally
discovered by projection-multi-compile
.
Currently automatic target generation functions are available for the following project types:
- projection (This simply presents available commands for the matching project types)
- CMake (& CTest)
- Make
- Poetry Poe
- Tox
Add embark integration to multi-compile using the multi-compile-embark extension feature. This allows you to immediately set one of the candidates show in a compile-multi session as the projects build, configure, etc. command type. Use this to interactively and incrementally update build targets.
Adds support for interactively selecting debuggable artifacts of a project and starting a debugger instance with dape.
Currently projection has very extensive support for certain project types. This tries to bind Emacs a little stronger into the framework and bring more IDE like support for extending the project builds. This section documents some of the extra support available.
For CMake projects projection supports the following extensions:
projection-cmake-set-preset
- Interactively sets a preset for a given build-type in the current project. By default if a project has any supported presets for a build-type projection will automatically prompt you for which to use and then cache it for subsequent invocations. Seeprojection-cmake-preset
to set an alternative preset behaviour for your use case.projection-cmake-set-build-type
- Alter the value of theCMAKE_BUILD_TYPE
option passed through to CMake while configuring.- Target resolution through the CMake file API. This requires a deterministic build directory and when unset will be disabled.
Project types are eioio objects. Every project type currently supported by
projection has a defvar
to allow you to modify it. For example you can override
the default compilation command run for a given project by overriding the build
attribute:
;; Change the test command for dotnet projects.
(oset projection-project-type-dotnet test "dotnet lint")
;; Unset the build command for dotnet projects.
(oset projection-project-type-dotnet build nil)
To remove a project type from the configuration list altogether you can delete it
from projection-project-types
.
(delq projection-project-type-cmake projection-project-types)