Skip to content

Projectile 3.1.0

Latest

Choose a tag to compare

@bbatsov bbatsov released this 04 Jul 06:21

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-kinds keyword of projectile-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) generalizes projectile-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-rspec and django project types ship with ready-made :file-kinds tables. Related files are keyed by their namespaced path, so a top-level UsersController relates to the top-level User model rather than an Admin::User.
  • projectile-find-file and projectile-find-file-dwim now 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 (which projectile-sort-order never reached).
    • Controlled by projectile-enable-frecency (default on); the per-project history is persisted in projectile-frecency-file and capped by projectile-frecency-max-files.
  • #1992, #1587, #1553, #1794: Add named project tasks - shell commands (or functions returning them) that you can run by name.
    • projectile-tasks maps task names to commands and can be set globally, per project type via the new :tasks keyword of projectile-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-order can 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 colocated jj+git repositories can be detected as jj by moving .jj first.
  • #1890: Recognize osc (openSUSE Build Service) checkouts: .osc is 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 like projectile-find-file-in-known-projects serve 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-notify watch per directory, bounded by projectile-watch-directory-limit.
  • Add projectile-other-window-command (s-p 4 4) and projectile-other-frame-command (s-p 5 5), modeled after Emacs's other-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-frame variant, e.g. s-p 4 4 x s starts a project shell in another window.
  • Add projectile-run-test-at-point (bound to c .), 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 via projectile-test-at-point-rules.

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 gnumake type's marker to GNU make's actual GNUmakefile spelling, and the make type now recognizes a lowercase makefile too.
  • projectile-auto-discover now defaults to t, so setting projectile-project-search-path is 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 under hybrid and only /-prefixed path entries took effect.
  • #1941: Dirconfig glob patterns now follow .gitignore-like rules, identical under native and hybrid indexing (previously native used loose string suffixes, so -build also ignored mybuild, 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-p and projectile-ignored-rel-p, the old pattern matchers superseded by the compiled dirconfig matcher (nothing referenced them anymore).
  • projectile-verify-file now goes through projectile-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-hook when projectile-dynamic-mode-line is enabled; change the option via Customize or setopt for it to apply immediately.
  • The recentf and recently-active sort 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 foreach on every alien/hybrid file listing; the cache invalidates automatically when .gitmodules changes and is also cleared by projectile-invalidate-cache.
  • User-facing conditions (no project found, missing optional package, nothing to toggle to, etc.) now signal user-error instead of error, so they no longer trigger the debugger under debug-on-error.

Bugs fixed

  • #1115: projectile-replace no 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-replace and projectile-replace-regexp now 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-locals now 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 on C-g.
  • CMake preset files referenced via include are now resolved relative to the file that includes them; previously the top-level CMakePresets.json's includes resolved against default-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-file no longer treats the dot before a file's extension as a wildcard when matching, so foo.el won't match an unrelated fooXel-style name.