Meta Redux: Projectile 3.4

Wait 5 sec.

Projectile 3.4 is out! Afterthe four releases that preceded it this summer, this one is polish rather than ambition.Most of it came out of an annoyance I’d been living with for years without ever quitenaming it: a lot of what I work on isn’t one directory. CIDER is really CIDER pluscider-nrepl plus orchard plus clj-refactor plus a handful of others. RuboCop isRuboCop plus rubocop-ast plus rubocop-rails and the other extension gems plus the styleguide. I’ve been maintaining families of repositories for the better part of a decade.Lately I’ve also been reaching for git worktrees far more than I used to, so the samerepository is now checked out two or three times on my disk at any given moment.Projectile saw all of that as a pile of unrelated projects, which is exactly what it lookslike from the outside.Somewhere else in the same projectprojectile-switch-worktree (s-p W) offers the other checkouts of the repository you’rein, each labelled with whatever tells it apart - the branch for git, the workspace name forJujutsu:Switch to worktree:~/src/myapp-main/ (main)~/src/myapp-hotfix/ (hotfix/crash-on-open)Git worktrees are the obvious case. Projectile asks git about those, so a worktree you’venever opened in Emacs shows up anyway. Jujutsu workspaces work the same way.The case I care about more is the one without any plumbing: a second git clone of thesame upstream. Same workflow, done by hand. I looked at my own ~/projects while writingthis and found four separate clones of CIDER sitting in it. Nothing records those anywhere,so Projectile can’t ask git to list them - it matches them among your known projects bytheir remote instead.Somewhere else in the same effortThe other half is projectile-switch-sibling-project (s-p n), which offers the projectsrelated to the one you’re in rather than every project on the machine.The interesting question was what “related” should mean. My first instinct was to comparedirectory names and look for a shared prefix. Before writing anything I tried thecandidates out on my actual project directory, all ninety-odd repositories of it.Comparing names does find rubocop, rubocop-ast and rubocop-rails. But grouping by theowner of the upstream remote finds this:Half of those names have nothing in common with cider, and no amount of staring atdirectory names would ever have related them.It needs a bound, though, and the same experiment showed why: about 40% of my checkouts areunder my own GitHub account, and “we’re both under bbatsov” doesn’t relate anything. So aninferred group covering more than a quarter of your known projects gets dropped and thenext signal takes over.1 If you’d rather just say what belongs together,projectile-project-groups is there and is never second-guessed.The commands come with youOnce Projectile knows two directories are the same repository, the command history canfollow you between them.Press M-p at the compile or test prompt in a worktree you made this morning and you getthe commands the project is actually built with, instead of an empty history. That’s anissue from 2022 that a stale bot hadhelpfully closed for me at some point.What doesn’t follow you is anything that runs without asking - what a prompt is pre-filledwith, and what projectile-repeat-last-command replays. I had those shared too in thefirst draft, and then watched a repeat in one worktree rebuild the tree next door. Aremembered command can carry absolute paths back to where it was typed.More languages, fewer surprisesprojectile-run-test-at-point (s-p c .) arrived in 3.1 knowing Python, Go and JS/TS. Itnow also knows Ruby (both RSpec and Minitest), Rust, Elixir, Java, Erlang and F#.Ruby is written the same way whichever framework you use, so there the project type picksthe runner rather than the syntax; Java’s picks between Maven and Gradle. Elixir tests getaddressed as FILE:LINE, because ExUnit can’t select a test by name from the command line.OCaml deliberately gets nothing - its tests are ordinary values you register with Alcotestor OUnit, so there’s no syntax to recognize.projectile-find-file-of-kind (s-p j) and projectile-toggle-related-file (s-p J)learned Phoenix, Laravel and Next.js. Rails and Django had been the only frameworks withfile-kinds tables out of the box, which felt like a strange place to have stopped.The reports answer backThe dashboard and the doctor from 3.3 both got a pass. They’re no longer plain text -section headers, field labels and findings are faced by meaning, with findings colored byseverity and sorted so anything wanting your attention comes first. The faces only inheritfrom standard ones, so your theme styles them without knowing Projectile exists.The doctor’s findings now come with a button that acts on them:[enable] for a projectile-mode you forgot to turn on, [enable caching] on a biguncached project, [open dirconfig], [edit .dir-locals.el]. Pressing one regenerates thereport. Findings Projectile can’t act on stay plain advice.And since a doctor report usually ends up pasted into an issue, w copies the buffer asplain text, without the faces and buttons.Odds and ends projectile-find-changed-file (s-p C) completes over what git reports as staged,unstaged or untracked - or, with a prefix argument, everything that differs from arevision you pick. projectile-run-task discovers rake tasks now, read out of your Rakefile and .rakefiles rather than by running rake -T, which would load the whole application. projectile-ignored-project-patterns is the regexp-matching sibling ofprojectile-ignored-projects, so keeping a whole area of your machine out of the knownprojects no longer needs a lambda. Messages Projectile emits on its own initiative are prefixed with [Projectile] now, andthe ones answering a command you just invoked aren’t. There were five differentconventions in there before, which I’d somehow never noticed. The known projects file, the frecency store and the session directory are resolved withlocate-user-emacs-file, so they land in the right place if your configuration lives in~/.config/emacs. Nothing moves if it doesn’t. Elixir’s implementation/test toggle offers foo_test.exs rather than foo_test.ex, ascript ExUnit will actually run. Project types can declare their test file extension now.Upgrade notesNothing here should break a working setup, but two things are worth knowing.A batch of options were renamed or folded together. The sixprojectile--use-comint-mode options became one projectile-use-comint-mode;projectile-per-project-compilation-buffer and projectile-per-command-compilation-bufferbecame projectile-compilation-buffer-scope; and a handful of options that had brokentheir own naming schemes were renamed to match their siblings.2 Every one of them isstill honored under its old name, so your config keeps working - you’ll just see anobsolescence notice.Two options are gone: projectile-tags-file-name and projectile-go-project-test-functionwere only ever read as Projectile loaded, which means setting them from your init fileafterwards did precisely nothing.And the command history is now the repository’s rather than the directory’s. Setprojectile-command-history-scope to project if you’d rather have it per directory.Histories you already have are adopted, not dropped.The full changelog ishere, and the manual isat docs.projectile.mx. The cross-repository features have apage of their own,limitations included.Five releases in six weeks is not a pace I intend to keep up, and this is the natural placefor the burst to land. The big pieces from 3.0 through 3.3 have had their corners sandeddown, and what’s left on my list is smaller and less interesting to write about. Which isroughly where a fifteen-year-old package ought to be.That’s all I have for you today. Keep hacking! Which is why projectile itself comes back with no siblings on my machine. The cap is working; the answer is a configured group. ↩ projectile-global-ignore-file-patterns, projectile-cmd-hist-ignoredups, projectile-related-files-fn-function, projectile-auto-discover, and the three reviewable-search options that were named after replace. ↩