Recording and Replaying Keystrokes
, I was working on the Forem codebase. Part of the toolchain at Forem is that we run Rubocop auto-correct on any files we commit.
We use Husky to manage our pre-commit hook.
With auto-correction, we sometimes might get a surprise change.
To avoid that surprise change, when we our linters, we try to remember to run the linters against the entire repository. That is not always something we do.
And I got one of those surprises. I wrote a pull request that helped avoid future sneaky auto-corrects.
But I want to write about what I did via Emacs and the command line.
The Steps in the Process
I took the following steps:
Run Rubocop Auto Correct
Rubocop—a Ruby code style checking and code formatting tool— has numerous “cops” that each check the code for one conceptual thing.
You can run Rubocop on a subdirectory and specifying a single “cop” to run. Below is the “cop” I ran on the app/models
directory, I specified to auto-correct any offenses:
$ rubocop app/models \
--only "Rails/RedundantPresenceValidationOnBelongsTo" \
--auto-correct
There output was as follows:
102 files inspected, \
27 offenses detected, \
27 offenses auto-correctable
With the --auto-correct
switch, the above call to rubocop
changed the code.
Run RSpec on a Directory
At Forem, we use RSpec. With rubocop
changing the models, I wanted to see how this impacted their specs.
$ rspec spec/models
There were 22 failures, which is less than the number of auto-corrects. Looking at the failures they all had the following form:
Failure/Error: it { is_expected.to validate_presence_of(:author_id) }
Excellent, these appear to all be consistent in structure.
There were two of the form it { is_expected.to validate_presence_of(:relation) }
. I found those when I re-ran the specs. And I quickly remediated them by hand.
Use Emacs to Quickly Remove the Specs
I ran the following commands:
consult-ripgrep
- Limiting to files in
spec/models
, I search forvalidate_presence_of _id)
. Myconsult-ripgrep
configuration uses fuzzy finding. In essence, my find will look for lines that have bothvalidate_presence_of
and_id)
in the same line. embark-export
- Export the search results to a new buffer.
kmacro-start-macro
- I recorded, by typing, the following key sequence
RET 2*C-k C-d M-o n
, more on that later. kmacro-end-macro
- End the recording of the macro.
-
kmacro-call-macro
21 times - Run the just recorded macro 21 times (one for each of the remaining specs to adjust).
M-x save-some-buffers
- Write the changes made in the embark buffer to their corresponding files.
For purposes of explaining what’s happening, I said I called kmacro-start-macro
, kmacro-end-macro
, and kmacro-call-macro
. That’s not quite true, I used kmacro-start-macro-or-insert-counter
and kmacro-end-or-call-macro
. Those are mapped (by default?) to F3 and F4. But from a writing and explaining standpoint, the underlying functions make a bit more sense.
The Keyboard Sequence
The key sequence for kmacro-start-macro
assumes:
- I’m starting in the search results buffer.
- I have two windows open, one for the search result buffer and one for “work”.
Below, I write about the key macro and what it does:
- RET
- Open the source file and line for selected search result.
- Ctrl + k twice
- Delete to the end of line and delete that now blank line. See jnf/kill-line-or-region for more details.
- Ctrl + d
- Delete the leading blank space.
- Alt + o
- Jump back to the search result buffer. See ace-window for more details.
- n
- Move to the next line in the search results buffer.
While recording keyboard macro, I could see what I was changing. I took my time to type and think what I wanted to do.
I recorded the macro, felt comfortable with it, and told Emacs to run it 21 more times.
I could have instead mashed on the F4 key twenty one (21) times.
Conclusion
First, I did all of this from a clean git
repository state. This allowed me to make potentially sweeping changes with confidence of being able to revert.
I often use Embark’s embark-export
; creating a buffer with a list of candidates. I can interact with the candidates, save the buffer for later, or further narrow those candidates with a different command.
What’s the list of candidates? In the above example, it’s search results. But it can be more than that.
I often pair embark-export
with wgrep. Wgrep allows you to edit a grep buffer and apply those changes to the file buffer like
Wgrep’s functionality is now a must have for my text editor.sed
interactively.
It took me over a year of using Emacs to even start recording and using keyboard macros.
In I wrote Principles of My Text Editor.
Nowadays, I’m usually recording a disposable macro every other day. The more I use Emacs the more I learn and adjust how I can tell Emacs to do more work for me.
Top comments (2)
Oh, so there's two of us using Emacs for Ruby development :D
There are several more that I know of (in particular software developers at research libraries).
I just wish I would have started earlier using Emacs (like in 2005 when a friend recommended it)