Projectile 3.1 is a feature release focused on making project navigation smarter and faster, with a batch of new commands inspired by the likes of VS Code, JetBrains and Zed. There are no breaking changes - every command and option from 3.0 still works.
Highlights: frecency-ranked file finding, named project tasks, run-the-test-at-point via tree-sitter, declarative "file kinds" (Rails models/controllers/views and the like), C-x 4 4-style other-window/other-frame prefix commands, and opt-in file-notify cache updates. The .projectile ignore rules now follow gitignore semantics consistently across indexing methods, and a round of TRAMP and indexing speedups landed too.
A few defaults and behaviors changed even though nothing broke API-wise, so please skim the Upgrading to Projectile 3.1 guide before you upgrade.
New features
- Add declarative "file kinds", letting a project type describe categories of files (e.g. Rails models, controllers and views) via the new
:file-kindskeyword ofprojectile-register-project-type.projectile-find-file-of-kind(j) prompts for a kind and completes over just the project files of that kind.projectile-toggle-related-file(J) generalizesprojectile-toggle-between-implementation-and-test: it jumps to related files of other kinds, jumping straight when there's one, prompting when there are several, and cycling through them on repeated presses.- The
rails-test,rails-rspecanddjangoproject types ship with ready-made:file-kindstables. Related files are keyed by their namespaced path, so a top-levelUsersControllerrelates to the top-levelUsermodel rather than anAdmin::User.
projectile-find-fileandprojectile-find-file-dwimnow rank the files you work with first, ordering completion candidates by how often and how recently you've visited them (with decay).- The ranking is applied through completion metadata, so it works with any completion UI and under every indexing method, including
alien(whichprojectile-sort-ordernever reached). - Controlled by
projectile-enable-frecency(default on); the per-project history is persisted inprojectile-frecency-fileand capped byprojectile-frecency-max-files.
- The ranking is applied through completion metadata, so it works with any completion UI and under every indexing method, including
- #1992, #1587, #1553, #1794: Add named project tasks - shell commands (or functions returning them) that you can run by name.
projectile-tasksmaps task names to commands and can be set globally, per project type via the new:taskskeyword ofprojectile-register-project-type, or per project via.dir-locals.el.projectile-run-task(c x) picks a task with completion and runs it like the lifecycle commands, in a per-task compilation buffer; a prefix argument lets you edit the command first (e.g. to pass ad-hoc arguments).projectile-repeat-last-task(c X) re-runs the project's last task.
- #978: Add
projectile-project-changed-functions, run whenever the current project changes - including implicitly via visiting a file or directory of another project - with the new and previous project root as arguments. - #1442:
projectile-sort-ordercan now be set to a function that receives the list of project files and returns them in the desired order. - #1984: The VCS markers are now customizable via
projectile-vcs-markers, whose order breaks ties between markers in the same directory - so colocatedjj+gitrepositories can be detected asjjby moving.jjfirst. - #1890: Recognize osc (openSUSE Build Service) checkouts:
.oscis now a VCS marker, a top-down-recurring root marker, and globally ignored; file listing uses the generic indexing command. - #1694: Add
projectile-invalidate-cache-all, which invalidates the caches of all known projects at once (handy when commands likeprojectile-find-file-in-known-projectsserve stale results). - #1075: Add experimental opt-in automatic cache updates via filesystem notifications, so files created, deleted or renamed outside Emacs update the cache without a manual
projectile-invalidate-cache.- Enable it with
projectile-auto-update-cache-with-watches; only local, cached projects are watched. - Each project uses one
file-notifywatch per directory, bounded byprojectile-watch-directory-limit.
- Enable it with
- Add
projectile-other-window-command(s-p 4 4) andprojectile-other-frame-command(s-p 5 5), modeled after Emacs'sother-window-prefix/other-frame-prefix(C-x 4 4/C-x 5 5): they display the buffer of the next command in another window or frame, keeping the Projectile keymap active for the next key.- This works with any command, including ones without a dedicated
-other-window/-other-framevariant, e.g.s-p 4 4 x sstarts a project shell in another window.
- This works with any command, including ones without a dedicated
- Add
projectile-run-test-at-point(bound toc .), which runs just the test around point, located via the buffer's tree-sitter parse tree (requires Emacs 29+ with tree-sitter).- Rules for pytest (
python-ts-mode),go test(go-ts-mode) and jest (js-ts-mode/typescript-ts-mode/tsx-ts-mode) are built in; other languages can be added viaprojectile-test-at-point-rules.
- Rules for pytest (
Changes
- Project root detection and project-type detection now probe marker files with a single directory listing per directory level instead of one file stat per marker, collapsing dozens of sequential round-trips over TRAMP into one.
- Marker matching is exact-case as a result, even on case-insensitive filesystems. This corrected the
gnumaketype's marker to GNU make's actualGNUmakefilespelling, and themaketype now recognizes a lowercasemakefiletoo.
- Marker matching is exact-case as a result, even on case-insensitive filesystems. This corrected the
projectile-auto-discovernow defaults tot, so settingprojectile-project-search-pathis enough to have those projects discovered (no change for anyone without a search path).- The scan now runs once per session on the first project switch, rather than on every switch, and remote (TRAMP) search-path entries are skipped.
- #1771, #740: Hybrid indexing now applies the dirconfig glob patterns (
-/!entries without a leading slash); previously they were silently ignored underhybridand only/-prefixed path entries took effect. - #1941: Dirconfig glob patterns now follow
.gitignore-like rules, identical undernativeandhybridindexing (previouslynativeused loose string suffixes, so-buildalso ignoredmybuild, and expanded globs per directory level, so matching differed from level to level):- a slashless pattern matches the file name or any directory segment at any depth;
- a pattern containing a slash is anchored at the project root (prefix with
**/to match anywhere); - a trailing slash matches directories only, and a matched directory covers its subtree;
*stops at/while**crosses it.
- Remove
projectile-check-pattern-pandprojectile-ignored-rel-p, the old pattern matchers superseded by the compiled dirconfig matcher (nothing referenced them anymore). projectile-verify-filenow goes throughprojectile-file-exists-p, so cold project-type detection benefits from the remote file-exists cache instead of issuing a TRAMP round-trip for every marker file probed.- The mode-line updater is only added to
window-configuration-change-hookwhenprojectile-dynamic-mode-lineis enabled; change the option via Customize orsetoptfor it to apply immediately. - The
recentfandrecently-activesort orders no longer rescan the full project file list once per recent file, making them usable on very large projects. - #1953: Cache the git submodule listing instead of shelling out to
git submodule foreachon every alien/hybrid file listing; the cache invalidates automatically when.gitmoduleschanges and is also cleared byprojectile-invalidate-cache. - User-facing conditions (no project found, missing optional package, nothing to toggle to, etc.) now signal
user-errorinstead oferror, so they no longer trigger the debugger underdebug-on-error.
Bugs fixed
- #1115:
projectile-replaceno longer skips replacements (or reports "All files processed" without replacing anything) when the project root is in abbreviated~/...form, or when a match's case differs from a lower-case input. - #1677:
projectile-replaceandprojectile-replace-regexpnow scan buffers that already visit a project file from the beginning, so matches before point in those buffers are no longer missed. - #1849:
projectile-skel-dir-localsnow exits its variable-entry loop on an empty variable name, keeping the entries made so far, instead of trapping the user in the prompt and discarding input onC-g. - CMake preset files referenced via
includeare now resolved relative to the file that includes them; previously the top-levelCMakePresets.json's includes resolved againstdefault-directory, silently dropping the included presets when the current buffer was outside the project root. - #1600: The default git submodule listing no longer depends on a Unix shell (single quotes,
tr), fixing alien/hybrid indexing of projects with submodules on Windows. - Invalidating a project's cache now cancels its pending deferred cache flush; previously a flush scheduled before the invalidation could fire afterwards and recreate the just-deleted cache file with empty contents.
- Frecency tracking now works for projects reached through a symlinked root; the visited file is resolved the same way as the project root, so its visits are no longer silently dropped.
projectile-find-other-fileno longer treats the dot before a file's extension as a wildcard when matching, sofoo.elwon't match an unrelatedfooXel-style name.