Kudos to @wvxvw for pointing me in the right direction. I was able to normalize the whitespace by advising ag-filter
, which ag adds to compilation-filter-hook
.
(defun ivan/filter-whitespace (prefix-pattern)
(save-excursion
(forward-line 0)
(let ((end (point)) beg)
(goto-char compilation-filter-start)
(forward-line 0)
(setq beg (point))
(when (< (point) end)
(setq end (copy-marker end))
;; normalize whitespace between prefix-pattern and rest of line
(while (re-search-forward prefix-pattern end 1)
(and (re-search-forward "[[:blank:]]*" end 1)
(replace-match " " t t)))))))
(defun ivan/filter-ag-whitespace ()
(ivan/filter-whitespace ag/file-column-pattern))
(advice-add 'ag-filter :after #'ivan/filter-ag-whitespace)
The structure of the filter is identical to ag-filter
itself, just the while
loop differs. I thought it might be nice to apply the same filter to the *Occur* buffer, but when all the results are from the same file I think the indentation lends worthwhile context.
The final result:

However, the downside is this breaks wgrep functionality, probably because wgrep is looking for an exact match. I had to rig up a workaround that recompiles the search results with whitespace intact when wgrep is engaged.
(define-key ag-mode-map
[remap wgrep-change-to-wgrep-mode] #'ivan/init-wgrep-mode)
(defun ivan/init-wgrep-mode ()
(interactive)
(advice-remove 'ag-filter #'ivan/filter-ag-whitespace)
(add-hook 'ag-search-finished-hook #'ivan/enable-wgrep-mode)
(recompile))
(defun ivan/enable-wgrep-mode ()
(remove-hook 'ag-search-finished-hook #'ivan/enable-wgrep-mode)
(advice-add 'ag-filter :after #'ivan/filter-ag-whitespace)
(wgrep-change-to-wgrep-mode))
sed
before displaying them, though maybe there's a better way.compilation-filter
to call some function on the text it is going to insert.