Project type support for Emacs builtin ~project.el~.
This Emacs package provides a [[https://github.com/bbatsov/projectile][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 [[https://cmake.org/][CMake]].
The Why? 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 :-).
Installation ** Manually
** MELPA Projection and any extension packages are on [[https://github.com/melpa/melpa][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)
Quickstart 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][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. :init (define-key global-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))
Features ** Projection Commands 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 [[file:src/projection-types.el][projection-types]] for how this is configured.
** Projection Hook 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.
** Projection ibuffer Offers variants of ~projectile-ibuffer~ and the [[https://github.com/purcell/ibuffer-projectile][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.
** Projection Find 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 [[file:src/projection-types.el]].
** Projection recentf A variant of =M-x recentf= for files exclusively in the current project.
** Projection multi-compile
[[https://github.com/mohkale/compile-multi][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 multi-embark
Add embark integration to multi-compile using the [[https://github.com/mohkale/compile-multi#compile-multi-embark][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.
** Projection dape
Adds support for interactively selecting debuggable artifacts of a project and starting a debugger instance with [[https://github.com/svaante/dape][dape]].
** Specialised Project Support 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.
*** CMake 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. See =projection-cmake-preset=
to set an alternative preset behaviour for your use case.
- =projection-cmake-set-build-type= - Alter the value of the =CMAKE_BUILD_TYPE=
option passed through to CMake while configuring.
- Target resolution through the [[https://cmake.org/cmake/help/v3.15/manual/cmake-file-api.7.html][CMake file API]]. This is disabled by default but
can be enabled by customizing =projection-cmake-target-backend= (for example:
=(setq projection-cmake-target-backend 'code-model)=) and then re-configuring the
project.
Configuration ** Permanently Configuring Project Types 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)