# Changelog

## master (unreleased)

## 3.2.1 (2026-07-13)

### Bugs fixed

* [#2094](https://github.com/bbatsov/projectile/issues/2094): Fix a `wrong-type-argument stringp` crash when running `projectile-search` (a 3.2.0 regression): the search-prompt tool tag now accepts the backend name symbol, not just a string.

## 3.2.0 (2026-07-12)

### 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](https://github.com/bbatsov/projectile/issues/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.
...
...
