Emacs is easy if you read code
is easy if you read code :en:emacs:ivy:counsel:
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
(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))) "\n" t))) ;; Need precise number of header lines for `wgrep' to work. (insert (format "-*- mode:grep; default-directory: %S -*-\n\n\n" default-directory)) (insert (format "%d candidates:\n" (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)
- The variable
regexis the regular expression built from the filter string you input. Please note that
regexis unquoted by
counsel-unquote-regex-parensso it can be used in shell. If you use
regexin Emacs Lisp, you don't need unquote it
- The variable
candsis the candidate lines created by running
regexas 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
(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.
wgrep-parse-command-results I got my own
(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
p4edit-in-grep-buffer to checkout files under Perforce control,
(defun p4edit-in-wgrep-buffer() "'p4 edit' files in wgrep buffer. Turn off `read-only-mode' of opened files." (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* ((filename (match-string-no-properties 1)) buf) (unless (string= filename fn-accessed) (setq fn-accessed filename) (shell-command (format "p4 edit %s" filename)) (if (setq buf (get-file-buffer filename)) (with-current-buffer buf ;; turn off read-only since we've already `p4 edit' (read-only-mode -1)))))) (forward-line 1)))))