Fold Search Results Away in rg.el

With the power of outline-minor-mode in Emacs, you can turn any text buffer into an outline – with the killer feature of “cycling”, i.e. folding and unfolding outline elements.

This includes rg.el-managed buffers, search results powered by ripgrep.

Update 2024-06-07: I changed the approach to a more robust regex! The form-feed insertion actually broke result navigation. compilation-next-error in turn would expect not to find a form-feed character at the start of a line. Working around that was more trouble.

To tell rg.el buffers which line is a ‘heading’ in terms of outline-minor-mode, change the regular expression to match each line beginning with "File:":

(defun ct/rg-folding ()
  "Enable outline-minor-mode on file entries in rg.el results."
  (setq-local outline-regexp "^File: ")
  (outline-minor-mode t))
(define-key rg-mode-map (kbd "<tab>") #'outline-cycle)

With that, you can run a search and zoom out to all files via M-x outline-hide-sublevels and then zoom into each with a tab.

Note that even though outline-regexp’s documentation says that we can assume we’re at the beginning of a line etc., omitting the ^ to denote the start of a line produces wonky results: it will fold, but sometimes, it would fold too much. I had file entries further down the buffer be folded away, too, and outline-hide-sublevels would hide the complete buffer.

My Previous Attempt That Is Actually Broken: Inserting a Form Feed

The default outline-regexp value contains the form-feed character. That gave me ideas

I now decorate all "File:" lines, prepending a form-feed character:

;;; Warning: this breaks navigation in result buffers!
(defun ct/rg-add-form-feed ()
  "Prepend a form feed character to all file match lines."
  (save-excursion
    (goto-char (point-max))
    (while (re-search-backward "^File: ")
      (goto-char (match-beginning 0))
      (insert "\n"))))
;; Do not add the hook to a broken function! :) 
; (add-hook 'rg-filter-hook #'ct/rg-add-form-feed)

You can enter a literal Unicode 0x0C FORM FEED (FF) character in Emacs by typing C-q C-l. I did that. But that doesn’t render well on the blog, so the code uses Unicode escape sequences. (You could also use "\f" I learned later!)

Form-feed lines between results, with the topmost ones folded away

I do render my form-feeds as lines, by the way. Check out form-feed-mode; the code from my init file is:

(use-package form-feed
  :ensure
  :delight
  :config
  (custom-set-variables
   '(form-feed-include-modes '(prog-mode text-mode help-mode compilation-mode org-mode)))
  :init
  (global-form-feed-mode +1))