Ripgrep is fantastic | Emacs is fantastic | BOOM you get the fantastic rg.el
Do you know
What does that mean?
Let's say we want to learn how to write Elisp macros because it seems
to fit with the problem we are facing. We've already read the info
node about macros (
M-x eval-expression RET (info "(elisp)Macros"))
that contains many examples but we want more examples.
We know that macros are defined with the macro
defmacro, so why don't
we search for calls of
defmacro in Emacs source code to get usage
examples of it?
In the directory where we've cloned Emacs source code we can run the
following command line (in a terminal) that searches directories for
\(defmacro that are of type
elisp, while respecting
ripgrep (the binary
rg '\(defmacro' -t elisp
This prints the following in the standard output:
admin/cus-test.el 304:(defmacro cus-test-load-1 (&rest body) lisp/align.el 1260:(defmacro align--set-marker (marker-var pos &optional type) lisp/custom.el 231:(defmacro defcustom (symbol standard doc &rest args) 389:(defmacro defface (face spec doc &rest args) 492:(defmacro defgroup (symbol members doc &rest args) 1139:(defmacro deftheme (theme &optional doc) ... MORE HITS HERE
Now, you do whatever you want with those matches in the terminal. You can browse the ouptut looking for some information, chose a match, the open the corresponding file and go to the appropriate line.
Everything in the terminal...
OR, you can use
rg.el and use its command
rg like this:
write the regexp:
select the directory where Emacs source code is,
elispas type file,
and you get the following buffer named
*rg* (in the mode
exactly the same matches as before with the command line:
-*- mode: rg; default-directory: "/tmp/emacs/" -*- rg started at Fri Apr 15 16:56:04 /usr/bin/rg [...] File: admin/cus-test.el 304 (defmacro cus-test-load-1 (&rest body) File: lisp/align.el 1260 (defmacro align--set-marker (marker-var pos &optional type) File: lisp/custom.el 231 (defmacro defcustom (symbol standard doc &rest args) 389 (defmacro defface (face spec doc &rest args) 492 (defmacro defgroup (symbol members doc &rest args) 1139 (defmacro deftheme (theme &optional doc) ... MORE HITS HERE
The difference is that now you can press (the bindings are defined in
next-error-no-select) to move to the next line with a match, show that file in other buffer and highlight the match,
previous-error-no-select) move to the previous line with a match, show that file in other buffer and highlight the match.
Assuming that we've look at some macro definitions, and we've seen
that the file
lisp/subr.el defines many of the macros that we have
already used in our own code like
when. Maybe to reduce
the numbers of macro to look at (
1505 calls to
defmacro - commit
504779f744ccc33c2177dafa34e21d83f6c640a0) we can consider only those
defined in the file
This can be done by modifying a little bit the previous command line like this:
rg '\(defmacro' lisp/subr.el
But, now that we are using
rg.el (INSIDE EMACS) we no longer want to
use directly the terminal for that task.
So, we go back to the buffer
*rg* and we press
f (bound to
rg-rerun-change-files) and we write
subr.el (the last part of the file
name), then we press
RET to rerun
rg with the same regexp as before
\(defmacro), but this time, only matches in the file
presented in the buffer
-*- mode: rg; default-directory: "/tmp/emacs/" -*- rg started at Fri Apr 15 17:29:43 /usr/bin/rg [...] File: lisp/subr.el 32 (defmacro declare-function (_fn _file &rest _args) 74 (defmacro noreturn (form) 81 (defmacro 1value (form) 88 (defmacro def-edebug-spec (symbol spec) 110 (defmacro lambda (&rest cdr) 136 (defmacro prog2 (form1 form2 &rest body) 143 (defmacro setq-default (&rest args) 163 (defmacro setq-local (&rest pairs) 193 (defmacro defvar-local (var val &optional docstring) 210 (defmacro push (newelt place) 225 (defmacro pop (place) 243 (defmacro when (cond &rest body) ... MORE HITS HERE
Now we've reduce our study of writing macros to 52 "classic" macros.
Still as before, we can use
p to look at those macro definition
*rg* buffer, the definition poping up in another buffer. For me,
this is insane. I love it.
But 52 is still an important number. We want to look at some macro definitions to be able to write our own macro. We need it. Let's almost right now.
Let's reduce that number. We observe that the file
defines also the macro
So, we decide to only look at the macro prefixed by
with- defined in
We can do it in the terminal running this command:
rg '\(defmacro with' lisp/subr.el
but we prefer doing it with
rg.el. So we go back to the buffer
and now we press
r (bound to
rg-rerun-change-regexp). This offers in
the minibuffer to modify the current regexp
\(defmacro. We modify it
\(defmacro with, we hit return, and we get the following 20
-*- mode: rg; default-directory: "/tmp/emacs/" -*- rg started at Fri Apr 15 17:49:57 /usr/bin/rg [...] File: lisp/subr.el 2092 (defmacro with-wrapper-hook (hook args &rest body) 3448 (defmacro with-undo-amalgamate (&rest body) 4188 (defmacro with-current-buffer (buffer-or-name &rest body) 4228 (defmacro with-selected-window (window &rest body) 4253 (defmacro with-selected-frame (frame &rest body) 4331 (defmacro with-output-to-temp-buffer (bufname &rest body) 4387 (defmacro with-temp-file (file &rest body) 4407 (defmacro with-temp-message (message &rest body) 4430 (defmacro with-temp-buffer (&rest body) 4445 (defmacro with-silent-modifications (&rest body) 4469 (defmacro with-output-to-string (&rest body) 4481 (defmacro with-local-quit (&rest body) 4549 (defmacro with-demoted-errors (format &rest body) 4712 (defmacro with-case-table (table &rest body) 4726 (defmacro with-file-modes (modes &rest body) 4738 (defmacro with-existing-directory (&rest body) 5297 (defmacro with-eval-after-load (file &rest body) 5417 (defmacro with-syntax-table (table &rest body) 6384 (defmacro with-mutex (mutex &rest body) 6570 (defmacro with-delayed-message (args &rest body)
We can now look at those definition trying to understand how macros are defined and how we can find ideas to solve our problem (either by writing our macro our deciding that a simple function might be enough...)
While we are looking at those macro prefixed by
with-, we remember
that we've seen another macro in another file that was matched in the
previous search and so visible in the "previous" contents of
buffer, and we want to look for it.
Do we have to redo everything (
M-x rg, ....)?
Still in the buffer
*rg* we can just visit backward and forward the
previous searches using
C-c < (bound to
Another mega cool feature of
ripgrep is the flag
short) that allows to include a number of lines before and after each
For instance, if we want to add 2 lines before and after the matches
of the regexp
\(defmacro with in the file
lisp/subr.el, in the
terminal we can run the following command:
rg --context 2 '\(defmacro with' lisp/subr.el
We can also do this with
rg.el. Let's go back to the buffer
(with the search that matches
\(defmacro with in the file
lisp/subr.el). Now we do the following:
rg-menu) that pops up a menu,
then in the minibuffer we see
--context=, we write
then we press
and this "reruns" the search adding the context around matches like this:
File: lisp/subr.el 2090- 2091- 2092 (defmacro with-wrapper-hook (hook args &rest body) 2093- "Run BODY, using wrapper functions from HOOK with additional ARGS. 2094-HOOK is an abnormal hook. Each hook function in HOOK \"wraps\" -- 3446- (cancel-change-group ,handle)))))) 3447- 3448 (defmacro with-undo-amalgamate (&rest body) 3449- "Like `progn' but perform BODY with amalgamated undo barriers. 3450- -- 4186- `(internal--track-mouse (lambda () ,@body))) 4187- 4188 (defmacro with-current-buffer (buffer-or-name &rest body) 4189- "Execute the forms in BODY with BUFFER-OR-NAME temporarily current. 4190-BUFFER-OR-NAME must be a buffer or the name of an existing buffer. -- 4226- (get-buffer-create (generate-new-buffer-name name) inhibit-buffer-hooks)) 4227- 4228 (defmacro with-selected-window (window &rest body) 4229- "Execute the forms in BODY with WINDOW as the selected window. 4230-The value returned is the value of the last form in BODY. -- ... MORE HITS HERE
When I think that life is amazing and then I look at all the work that has already been done everywhere, I think wowwww, this is really amazing.
I want to thank you all for all the great programs that lives with us thanks to your imagination and your work.
ripgrep is fantastic.
Emacs is fantastic. BOOM you get the fantastic
What can we add to this paradise?
We can add org-mode to the party.
Yes, if you try to open the following org link (in a org-mode buffer),
Emacs will ask you to confirm if you want to execute this elisp form,
and by answering yes the result of an
rg search will pops up in
buffer like we did previously (assuming Emacs source code is clone
under the directory
[[elisp:(rg-run "\\(defmacro with" "subr.el" "/tmp/emacs/" nil nil '("--context=2"))]]
WE ARE DONE!!!