If you regard a package as a collection of APIs and read its code, Emacs is easy to master.

For example, here is a useful tip on using counsel-ag and wgrep to edit multiple files I recently learned.

To understand this black magic, you only need know counsel-ag-occur from counsel.el (v0.9.1):

(defun counsel-ag-occur () "Generate a custom occur buffer for `counsel-ag'." (unless (eq major-mode 'ivy-occur-grep-mode) (ivy-occur-grep-mode)) (setq default-directory counsel--git-grep-dir) (let* ((regex (counsel-unquote-regex-parens (setq ivy--old-re (ivy--regex (progn (string-match "\"\\(.*\\)\"" (buffer-name)) (match-string 1 (buffer-name))))))) (cands (split-string (shell-command-to-string (format counsel-ag-base-command (shell-quote-argument regex))) "

" t))) ;; Need precise number of header lines for `wgrep' to work. (insert (format "-*- mode:grep; default-directory: %S -*-





" default-directory)) (insert (format "%d candidates:

" (length cands))) (ivy--occur-insert-lines (mapcar (lambda (cand) (concat "./" cand)) cands)))) (ivy-set-occur 'counsel-ag 'counsel-ag-occur) (ivy-set-display-transformer 'counsel-ag 'counsel-git-grep-transformer)

Inside counsel-ag-occur :

The variable regex is the regular expression built from the filter string you input. Please note that regex is unquoted by counsel-unquote-regex-parens so it can be used in shell. If you use regex in Emacs Lisp, you don't need unquote it

is the regular expression built from the filter string you input. Please note that is unquoted by so it can be used in shell. If you use in Emacs Lisp, you don't need unquote it The variable cands is the candidate lines created by running ag with regex as parameters in shell

is the candidate lines created by running with as parameters in shell Then a wgrep-friendly buffer is created

After spending 5 minutes to understand the internals, you can easily implement similar features.

Now let's develop our own black magic by enhancing the wgrep-friendly buffer.

My project uses Perforce as VCS. So I need check out files and make them writable before using wgrep.

Read code of wgrep.el (v2.1.10),

(defun wgrep-prepare-context () (save-restriction (let ((start (wgrep-goto-first-found)) (end (wgrep-goto-end-of-found))) (narrow-to-region start end) (goto-char (point-min)) (funcall wgrep-results-parser))))

wgrep-results-parser is actually alias of wgrep-parse-command-results whose code is too much to paste here. You can M-x find-function wgrep-parse-command-results to read its code.

By combining wgrep-prepare-context and wgrep-parse-command-results I got my own access-files-in-wgrep-buffer :

(defun access-files-in-wgrep-buffer() (interactive) (save-restriction (let* ((start (wgrep-goto-first-found)) (end (wgrep-goto-end-of-found)) fn-accessed) (narrow-to-region start end) (goto-char (point-min)) (unless (featurep 'wgrep) (require 'featurep)) (while (not (eobp)) (if (looking-at wgrep-line-file-regexp) (let* ((fn (match-string-no-properties 1))) (unless (string= fn fn-accessed) (setq fn-accessed fn) (message "File relative path=%s" fn)))) (forward-line 1)))))

You can replace the line (message "File relative path=%s" fn) to (shell-command (format "any-shell-cli %s" fn)) to do anything on the files.

You can insert definition of access-files-in-wgrep-buffer into your .emacs and run M-x access-files-in-wgrep-buffer in wgrep buffer to have a test.

For example, I modified access-files-in-wgrep-buffer to p4edit-in-grep-buffer to checkout files under Perforce control,