Skip to content

v3.2.0

Choose a tag to compare

@bbatsov bbatsov released this 12 Jul 10:54

New features

  • Add reviewable read-only search commands, a search-only sibling of the reviewable replace UI.
    • projectile-search-review (s R) searches for a literal string; projectile-search-regexp-review (s X) searches for an Emacs regexp, honoring full Emacs regexp syntax.
    • Matches are gathered into a read-only *projectile-search* buffer, grouped by file, one LINE:COL: CONTEXT line per match with the matched span highlighted; there is no preview, no per-match toggle and no apply.
    • The buffer reuses the replace reviewer's navigation, case/regexp toggles (c/x), line and file filters (k/d/K/D), re-search (g) and grep-mode export (e).
    • r bridges the current search to the replace reviewer, carrying over the term, literal-ness and case setting and prompting only for the replacement.
    • A literal projectile-search-review accelerates its scan with ripgrep when rg is installed, streaming matches in near-instantly on large projects.
      • Controlled by projectile-search-use-ripgrep (default on); set it to nil to always use the pure-elisp scan.
      • The ripgrep fast-path follows ripgrep's ignore rules plus Projectile's ignore globs, which can differ slightly from the elisp path's file set (e.g. hidden files, symlinks); regexp search and the whole replace reviewer always use the portable elisp scan.
    • The commands are available from projectile-dispatch and the Projectile menu.
  • #1924: Add reviewable project-wide replace commands that let you preview matches and choose which to apply, instead of the blocking, file-by-file query-replace walk of projectile-replace.
    • projectile-replace-review (R) does a literal replace; projectile-replace-regexp-review does an Emacs-regexp replace whose replacement can reference capture groups.
    • Matches are gathered in Emacs Lisp, so the regexp command honors full Emacs regexp syntax and the preview reflects exactly what will be edited, including unsaved changes in open buffers.
    • The *projectile-replace* results buffer shows a per-file, per-match preview where each match can be toggled on or off; ! (or C-c C-c) applies just the enabled ones, in any order.
    • Applying edits each file from the bottom up, edits open buffers in place under a single undo step, writes closed files back preserving their coding system, and skips buffers modified since the search rather than corrupting them.
    • The results buffer can reshape the search in place, each action re-scanning and re-rendering the previews:
      • c toggles case sensitivity (seeded from case-fold-search).
      • x toggles between literal and Emacs-regexp matching, refusing an invalid regexp rather than erroring.
      • k/d keep or flush the matches whose line matches a regexp; K/D do the same by file name; re-searching with g restores anything filtered away.
    • A status line at the top shows the term, replacement, match and file counts, the mode flags, and a note when the list has been filtered.
    • e exports the shown matches to a grep-mode buffer so wgrep or Emacs 31's grep-edit-mode can turn them editable and write back; wgrep stays an optional integration and ! remains the no-dependency apply path.
    • Both reviewers scan the project asynchronously, so a large search no longer freezes Emacs.
      • The results buffer opens right away, matches stream in as they're found (the header shows a "Searching..." progress note), and the scan is cancelable with q, C-g, or by killing the buffer.
      • While a scan is still running, ! (apply), e (export) and the filter keys (k/d/K/D) refuse until it finishes, so the write-back never runs against a partial match set and a filter can't be outrun by a later chunk; starting a new scan (g/c/x) cancels any in-flight one.
      • Set projectile-replace-async to nil to force the old synchronous single-pass scan; batch runs always scan synchronously.
    • The commands are available from projectile-dispatch and the Projectile menu, and the match cap is customizable via projectile-replace-max-matches.
  • Add projectile-session-mode, a global minor mode that gives each project its own tab-bar tab.
    • Switching to a project selects its existing tab (restoring that project's window layout) when one is open, or otherwise opens a fresh tab named after the project and populated via projectile-session-default-action.
    • Same-named checkouts get distinct tab names (e.g. work/foo and home/foo); customize the scheme with projectile-session-tab-name-function.
    • projectile-session-switch-to-buffer completes over just the current tab's project buffers.
    • Sessions persist across Emacs restarts: projectile-session-save, projectile-session-restore and projectile-session-forget write a project's window layout and buffers to a file under projectile-session-directory and read it back; projectile-session-save-all saves every open project at once.
    • Switching to a project with a saved session restores it (gated by projectile-session-restore-on-switch); projectile-session-autosave saves sessions on switch-away and on exit.
    • projectile-session-restore-all reopens every saved project's session, each into its own tab (skipping projects already open and dropping stale sessions whose files are gone); set projectile-session-restore-on-startup to run it automatically at Emacs startup.
    • Which buffers are recorded, and how, is extensible via projectile-session-buffer-serializers; file-visiting and dired-mode buffers work out of the box.
    • The session commands are bound under the w sub-prefix of the command map (s-p w s, s-p w S, s-p w r, s-p w R, s-p w f, s-p w b), and are also available from projectile-dispatch and the Projectile menu.

Changes

  • Add projectile-frecency-max-projects (default 100), which caps how many projects' frecency history is kept so the store can't grow without bound.
  • projectile-replace-scan-chunk-size is now a user-facing defcustom (was an internal variable).
  • Advertise the right completion metadata category per command so marginalia and embark annotate and act on candidates correctly: buffer switching now uses the buffer category (so marginalia shows its rich buffer annotations), project switching uses file, and file commands keep project-file - previously every completion was hardcoded to project-file. projectile-completing-read gained a :category keyword for this.
  • Add whole-word matching to the reviewable search/replace: toggle it with w in the results buffer, default it with projectile-search-whole-word, or seed it for one run with the projectile-dispatch --word switch. The elisp scan fences the pattern with word boundaries and the ripgrep fast-path passes --word-regexp, so both engines agree.
  • Wire the reviewable search/replace into the projectile-dispatch Modifiers: the two literal/regexp search entries are folded into one sR driven by --regexp (matching how --regexp already drives the ag/ripgrep search), and a new --case-sensitive switch seeds the reviewable search/replace case-sensitive. Both can still be flipped with x/c in the results buffer.
  • Highlight the search tool and the default value in the search prompts, and give them one consistent Search [tool] for (default: X) format across projectile-search, projectile-grep/-ag/-ripgrep and the reviewable search. The tool that will run (grep/ag/ripgrep, or ripgrep/elisp for the reviewable search's literal fast-path) is faced with projectile-search-prompt-tool and the symbol-at-point default with projectile-search-prompt-default. The reviewable search now also shows its default up front and which engine it will use.

Bugs fixed

  • #1549, #1676: Re-invoke a function-valued lifecycle command (e.g. a CMake preset picker or a project type's :run/:test function) on every run instead of freezing its first result in the command cache, so it can prompt again.