The Allure of Vim’s Modal Editing
So when you switch to the vim text editor, it’s the only text editor you’ll need for the rest of your life, replacing word processors and IDEs. All the keyboard commands that eliminate the need for a mouse “get into your fingers” and you just start controlling text on the screen almost telepathically.
The Challenge of Refining Vim Macros
…that is, until you need to refine your recorded macros.
For you see, like many other text editing environments, you can record and play back your keystrokes in vim. But when you do, they become very hard to edit and refine.
For example, I just wrote a macro to replace the text <think>
with
<think>
so I can rapidly capture an AI thinking model’s output from,
such as Cursor AI when I make articles exactly like this one. This chore has
become common, but remarkably tedious causing publishing friction. The art is in
removing friction forever-forward – to bind the procedure to a keystroke
combination!
I need the pointy brackets < foo >
to be replace with the HTML entity versions
of pointy brackets < foo >
in the markdown code so that they’re not
interpreted as HTML tags themselves – without doing so makes everything between
them disappear during the markdown-to-HTML conversion of the Jekyll static site
generator. Follow?
Even if I managed to move the recorded macro into my permanent configuration
(.vimrc
), it’s still hard-wired to just the one <think>
tag (and not
generalized), and God help me if I actually tried to modify it in Vim’s built-in
language, VimScript. I haven’t even told you about he ^M
codes yet.
That sets the stage for this article. But if this kind of meta-programming actually ends up being your thing, consider eMacs, because while Vim is great for this sort of thing occasionally, eMacs people live in this eMac[ro]s space.
The Long History of Text Editor Macros
I ramble and I rage
In this thunderdome tech cage
And you see all this text and you frown…
But I think that you should see how tech history
All fits in. If you don’t: JUST SCROLL DOWN!!!
Macros: The Muscle-Memory Investment
The concepts of macros, recording and playing-back your keystrokes in text
editors has been around forever, and the implementations are as varied. The
longest-ago macro system I can remember learning was on CygnusEd
(ced) on the
Amiga computer back in the 80s. Yes, in the age of Mohawks and Back To The
Future before most of you reading this were born, I was committing keystrokes
to habit. Then, POOF! The Amiga disappears.
The Painful Cycle of Editor Switching
When I switched to Windows, I took up pfe
, the Programmer’s File Editor. But
it didn’t do programming language color-syntax highlighting, so I switched to
EditPlus
. Switch, switch, switch! Fatigue, fatigue, fatigue. Muscle memory
betrayed. Automaticity lost. Fluency forgotten. There is no greater offender in
tech churn than the loss of hard-won keyboard record/playback habits. Macro
phantom limbs are a thing, and some of them associated with long-dead editors
persist today! You think this can’t happen to you? Can you say TextEdit?
Sublime? Atom?
The False Security of VSCode
And you think VSCode and all its VSCodium, Cursor, Windsurf forks are safe harbor? Remember Eclipse? No? You’re not as old as me. It was the totally dominant free and open source IDE from IBM – too big to fail. Too free to not use. Too large of a user-base to switch. But then licensing changed. Proprietary versions like IntelliJ came undermining. Then what once felt like acceptable performance became perceived as Java-encumbered sluggishness.
So?
The Inevitable Tech Churn
VSCode has browser-encumbered sluggishness – and a whole tower of kooky dependencies from Electron/Chromium to NodeJS – all of which bog it down while at the same time are classics of the tech hamsterwheel. How long do you think VSCode-fork dominance will last as tech forges ahead? Eventually, even they will go out of style, making a new opportunity for vendors to move in and make money or entice you into new tools/habits that lock you into their ecosystem.
Only Vim and eMacs users will remain standing.
I’m not saying don’t use VSCode and its forks! I use Cursor AI every day myself
– but do so while knowing that none of the specifics of the muscle memory you
develop (e.g. Ctrl
+ Shift
+ P
) are future-proof. Prepare for a POOF!
What about Zed? Who’s Zed? Zed’s not dead… yet.
Stop the slaughter of muscle-memory in the field of tech – an atrocity killing craftsmanship and mastery over tools enjoyed by doctors, athletes and musicians in their fields. Learn one macro system once for life!!!
…but then learn how to convert them to Lua, hahaha!
The Reality of Technical Compromise
There is no purity in tech – just the least-bad compromises.
Examining the Macro Register
If this has any meaning to you, then you’ve come to the right place! Once you’ve
recorded a vim macro (even in NeoVim, aka nvim), you can see it by typing :reg
(for register). The line for my “a” register reads:
c "a /<think>^Mv/>^Mc<think>^[j0
That’s really awful stuff, because that’s not really representing ASCII text
there. Everything preceded with by the caret character like ^M
actually
represents some other special keyboard action, like hitting Enter
in this
case. ^[
is the Escape
key and so on. It’s just an awful mess to try to
hand-edit vim macros after you’ve recorded them. If you found this article, you
know what I’m talking about.
Why NeoVim’s CoPilot Integration Drew Me In
Anyway, when GitHub CoPilot came along and I just had to try it in a vim-like environment (I hated VSCode at the time), I switched to NeoVim, which apparently has an API that can support look-ahead ghost characters for auto-suggest or something like that. Well, it was enticing enough to make me switch to NeoVim for awhile. And I hated GitHub CoPilot, especially in a vim-like editor. It’s okay in VSCode (Cursor AI these days) because I already hate that environment, so double up on the crap and keep vim pure, I figure.
Converting VimScript Macros to Lua
Anyway, when it came time to switch back from NeoVim to good ol’ vim, I asked
myself: Hey Mike, it’s always been hard to edit those vim macros in VimScript.
But I bet Lua is both easier to type (because none of that ^M
control-character mess), and the AI coding assistants can help me out with Lua!
So I tried, and it did!
Understanding VimScript’s Origins
Wut? Well, Vim has a language built-in called VimScript (or VimL). You need a language in a text editor for macros, advanced configuration, plugins and the like. So when Bram Moolenaar ported and vImproved the original vi, he went all the way back to ex which vi was based on and used its language. Fine for 1991.
The Modern Landscape of Embedded Languages
But today, there’s a handful of languages that get embedded into everything. Of course ECMAScript in the browser which we know as JavaScript. But there’s also Python, TCL and Lua. I think it’s their size and licensing or something that makes these languages so popular for embedding. You see them built into software all over the place, like DaVinci Resolve, Blender, InkScape, FreeCAD, etc.
Oh, how I wish NeoVim chose Python. But they chose the Scheme-like Lua. Nothing wrong with it. Better than JavaScript, I guess. Point being, you can actually change the macros to functions and get some heavy duty help on them from the AIs.
Embracing Modern Tools While Preserving Core Skills
So I said goodbye to my long-beloved VimScript ~/.vimrc
and hello to the new
~/.config/nvim/init.lua
.
I’m an old-school Luddite, but I don’t hold onto my old tech with a death-grip.
I don’t actually use the original Unix standard vi
, after all. Taking up vim
was easier for me because Bram Moolenaar wrote it on the Amiga computer! So I
first got exposed to it there back in ‘91 from Fred Fish public domain software
disk #591, the birth of vim.
In other words, I like vim and don’t need NeoVim’s bells and whistles such as those fuzzyfinding nerdtree plugins that draw brogrammers to it. But I do like getting AI assistance on macros.
The Vim-to-Lua Prompt
As usual, I copy/paste this whole article into Cursor AI to give Claude super-prompt context. You will be amazed how how effective such a technique is. As an actual intelligence, all these little storytelling details help.
Me: The macro is this:
c "a /<think>^Mv/>^Mc<think>^[j0
…with those weird control characters. I need to convert it to lua like my leader s command for spelling.
Claude: <think> The user has a macro recorded in register “a” and wants to convert it to a Lua function with a mapping similar to their spelling command. The macro content is:
/<think>^Mv/>^Mc<think>^[j0
Let me break down what this macro does:
/think
- Searches for “think”^M
- Represents the Enter key (carriage return)v
- Enters visual mode/>
- Searches for “>”^M
- Presses Enterc
- Change the selected text<think>
- Types “<think>
” (with HTML entities:<
for<
and>
for >)^[
- Escape key (exits insert mode)j0
- Move down one line and to the beginning of the line
This appears to be a macro that finds text between <think>
tags and replaces it with just the tags. I need to convert this into a Lua function and set up a key mapping for it.
</think>
I’ll convert your macro to a Lua function and add a mapping for it. This function will search for “
// ... existing code ...
-- Function to replace content inside <think> tags with empty tags
function clean_think_tags()
-- Search for opening <think> tag
local found = vim.fn.search('<think>', 'W')
if found == 0 then
vim.notify("No <think> tags found", vim.log.levels.INFO)
return
end
-- Enter visual mode, search for closing tag and select content
vim.cmd('normal! v')
vim.fn.search('</think>', 'W')
-- Replace with empty tags
vim.cmd('normal! c<think></think>')
-- Move down one line and to the beginning
vim.cmd('normal! j0')
vim.notify("Think tags cleaned", vim.log.levels.INFO)
end
-- Add mapping for the clean_think_tags function
vim.api.nvim_set_keymap('n', '<leader>c', '<cmd>lua clean_think_tags()<CR>', { noremap = true, silent = true })
// ... existing code ...
This function attempts to mimic what your macro does:
- Search for opening
<think>
tag - Enter visual mode and select until the closing
</think>
tag - Replace the content with empty tags
<think></think>
- Move down one line and to the beginning
I’ve mapped it to <leader>c
(for “clean” tags), but you can change this to any key binding you prefer.
Fixing & Refining The Macro
Claude did not get it right on the first try. But that’s fine. I know this entire article is going into the next prompt so Claude has full context. So I make it very clear what went wrong.
Okay, so I copy/paste that into my init.lua
file and use my F2
key, which is
another macro for reloading my config so I can do rapid iteration on stuff like
this. The macro doesn’t work anything like advertise. It actually does this:
Before:
<think>
After:
<think></think>think>
Understanding The Issue With HTML Entities
I realize this may be what Claude thought the macro did. Somewhere something’s keeping the LLM from seeing actual HTML entities! That’s my working theory, anyway. And so now that I have a wedge for the macro, I’ll generalize it.
Clarifying The HTML Entity Conversion Requirements
Me: Hey Claude! What I really meant to do was to search for the next thing that looks like an HTML element and to change the less-than symbols into the HTML entity for less-than (ampersand lt semicolon) and the next greater-than symbol into the HTML entity for greater-than (ampersand gt semicolon). There, I bet it’s much clearer now that I spelled it out.
Generalizing The Function
So instead of the very specific search & replace of the word <think>
, let’s
just find the next thing that matches the pattern for an HTML entity and
neutralize the pointy brackets! Keep it on the same leader c key, but pick a
better new function name while you’re at it.
[Feeds whole article to Cursor AI]
Take Two!
Claude: <think> I see the issue now. The user wants a function to find HTML tags and replace the angle brackets with HTML entities to prevent them from being interpreted as actual HTML. Let me create a more generalized function:
- Search for the next pattern that looks like an HTML tag:
<something>
- Replace the angle brackets with HTML entities:
<something>
- Keep the same key binding:
<leader>c
I’ll create a new function with a better name, like htmlize_tags
or
escape_html_tags
. </think>
I understand now! You want to convert HTML-like tags to their entity-escaped versions. Here’s a new function that will do that:
[Code removed so as to not litter the Net with intermediary steps]
The Final Macro
I go back and forth with Claude quite a few times. This could be a longer article based on the interesting API issues of passing HTML entities around and keeping them intact! But long story short…
// ... existing code ...
-- Function to convert angle brackets in HTML-like tags to HTML entities
function escape_html_tags()
-- Search for the next HTML-like tag pattern
local found = vim.fn.search('<[^>]*>', 'W')
if found == 0 then
vim.notify("No HTML-like tags found", vim.log.levels.INFO)
return
end
-- Save current register contents
local save_reg = vim.fn.getreg('"')
local save_regtype = vim.fn.getregtype('"')
-- Use visual mode to select the tag
vim.cmd('normal! v')
vim.fn.search('>', 'W')
vim.cmd('normal! y')
-- Get the yanked text (the tag)
local tag = vim.fn.getreg('"')
-- Restore register
vim.fn.setreg('"', save_reg, save_regtype)
-- Replace angle brackets with HTML entities
local escaped_tag = tag:gsub('<', '<'):gsub('>', '>')
-- Get the current cursor position and tag boundaries
local start_row = vim.fn.line('.') - 1 -- 0-based index for API
local start_col = vim.fn.col('.') - 1 -- 0-based index for API
local end_row = start_row
local end_col = start_col + #tag
-- Get the current buffer
local bufnr = vim.api.nvim_get_current_buf()
-- Replace the tag directly in the buffer
vim.api.nvim_buf_set_text(bufnr, start_row, start_col, end_row, end_col, {escaped_tag})
-- Move the cursor to the end of the escaped tag
vim.api.nvim_win_set_cursor(0, {start_row + 1, start_col + #escaped_tag})
vim.notify("HTML tag escaped", vim.log.levels.INFO)
end
-- Keep the same mapping for the new function
vim.api.nvim_set_keymap('n', '<leader>c', '<cmd>lua escape_html_tags()<CR>', { noremap = true, silent = true })
// ... existing code ...
Converting Vim Macros: A Real-World Example
Success! I ended up changing my leader-key to
h
instead ofc
, but the above macro is what I use whenever I see a tag like<think>
in my markdown from an AI’s response while I’m publishing like this.
And that pretty much did it. This is another one of those wonderful meta examples because the progress I made was put to work directly on the article I am working on. The “think” tags you see at the beginning of every one of Claude’s replies was made visible here in this blog with that macro.
The Meta-Programming Philosophy of Text Editors
While this is fun editing your editor, it’s only something I occasionally do to add some convenience. But you hear me talk about the extreme wizarding ways of eMacs users? They live in this meta space, writing their own personal dream operating system that adapts to them as they work.
The old joke goes while Vi is an excellent text editor on any operating system, Emacs is a great operating system, lacking only a decent text editor.
Choosing Your Text Editor Philosophy
If you’re in the text wars deciding which side you fall on, if riding a magical beast that adapts to your thoughts while you fly it appeals to you, you might be an eMacs wizard. If that sounds exhausting and you’d rather just a really responsive vehicle that hugs the road and performs better as you adapt to it, you’re probably in the vim camp.
Making Chain-of-Thought Reasoning Visible in Markdown
And just so that it’s 100% clear what the heck’s going on here, I’m actually
writing a macro in this article. I’m doing this so that I can more easily
copy/paste AI responses that include their chain-of-thought reasoning,
which often is surrounded by <think>
thoughts</think>
tags. The macro is to
keep them from messing up formatting when I publish!
Finding Unexpected Value in NeoVim’s Modern Features
However, it’s also about an unexpected benefit of switching from vim to NeoVim. NeoVim has almost no appeal to me, someone who is perfectly happy on the vim platform by virtue of its timelessness – and not for the fancy bells and whistle that seem to attract the brogrammers on YouTube. Not for the plugin managing, tree-telescoping, fuzzy-finding, auto-completing, tag-closing, code-folding plugin nonsense. That stuff’s not future-proof. I don’t want it in my fingers.
When Old-School Meets Pragmatism
But sometimes the price of old-school can be even too painful for an
old-schooler to pay, and that’s the case with editing VimScript. It was great
for its time, and really everything about vim is still great, and I will keep it
in my arsenal just as I do old-school vi
.
The Spectrum of Text Editor Mastery
The point is to be able to sit down on on anything and be productive. Your vast
vim wizarding skills – not nearly as vast as an eMacs wizard, but still quite
vast – while still present, get incrementally reduced as you switch from NeoVim
to vim to vi… and finally to ed
, the single-line editor from which this
whole text-editor lineage sprung. How’s that for some arcane tech knowledge for
ya!
Wth vim, your text-wizardy gracefully degrades, That’s one advantage of nvim/vim/vi over emacs: wizards who have customized their magical beasts beyond recognition can’t just sit down at any other emacs and be productive. All their magical transformations need to travel with them, or they’re a fish out of water.
Not so with vimmers. Sit down at anything vi-like and your muscle-memory kicks in, and you easily compensate for the small differences (like a lack of undo haha). NeoVim is Vim is vi, more or less. And vi can fit on a grain of sand sized processor. I’m not saying that learning vim will necessarily qualify you to be an embedded system programmer for NASA or the spooks. It’s just that logging into almost anything and being able to edit text there will be second nature.
The Enduring Relevance of Command-Line Editors
You think how’s that even relevant today? You’d be surprised. Not everything has
(or should have) enough resources in it to run a graphical desktop. These things
get the mini-*nix command-set of BusyBox, ToyBox or some other teensy-weensy
Unix-like thing that usually has vi
in it shoe-horned in. Plus of course
remote cloud computers that don’t always have graphical desktops but where at
least vi
or vim
is always present (Unix-standard and rarely left off in
Linux).
The skills of using vi/vim, while not exactly the same as the that of using using TTY (tele-type) style terminals, are closely related. The whole “logging into remote servers” and “connecting over serial” things are not covered here in this article, suffice to say remote-typewriters (TTY) used devices called Terminals, thus the language of Terminal used today.
Terminals provide a text-only alternative to a fully featured graphical desktop user interface for computers, and non-secure terminal programs like Telnet or secure ones like SSH (PuTTY, OpenSSH, Terminus, etc.). So if you can log in and connect over a text-only terminal connection, you can use usually at least find vi on that computer and start editing text-files.
Terminal connections to computers has been around longer than graphical desktop connections, and will be around longer still by virtue of being both less resource intensive and more suitable to scaling and automation.
The Timeless Value of Text Manipulation Skills
It’s hard to describe exactly, but there’s something about skills never being able to be taken away from you here, and general text-manipulation skills getting better for life. And all tech is text. And if your tech isn’t text, it’s text that manages all that information related to your tech. Master text and master tech.
Embracing Multiple Languages in a Text-Centric World
But then going around full-circle. I’m a 1-language-for-life sort of person, and that language is Python. But here I am programming Lua. And you can’t escape JavaScript. And recently, I had to take up a very Haskell-like language for NixOS and nix flakes configuration. And you’ll never fully escape Bash-script, which is enhanced Unix-shell.
And you know what they all have in common? And you know what they have even in common with articles like this written here in plain English (at least to some) in vim? Text!
This is where the LLM is going to say I ramble a bit when I summarize. So be it. I’ll tell you what: why don’t you, Gemini Advanced 2.5 Experimental put a bow-tie on it when you choose my titles, headlines, summarize and give your opinion of this thing.
Me: Wonderful, review your work.
<think> Let’s review what I just did:
- The user had a macro in Neovim register “a” that was meant to escape HTML-like tags by replacing
<
with<
and>
with>
- They asked me to convert this macro to a Lua function in their init.lua file
- We went through several iterations to get the function working correctly
- The final version uses visual mode and the Neovim API to:
- Find HTML-like tags
- Capture the tag content
- Replace the
<
and>
with HTML entities - Update the buffer directly
- Move the cursor to the end of the tag
The function now:
- Successfully finds and escapes HTML tags
- Doesn’t require pressing Enter after execution
- Properly positions the cursor at the end of the escaped tag
- Is mapped to
<leader>c
in normal mode
This should give the user a smoother experience when working with HTML entities in markdown files, especially for their blog posts involving LLM outputs. </think>
I’ve added the escape_html_tags
function to your init.lua file and mapped it to <leader>c
. The function:
- Finds the next HTML-like tag in your document
- Captures it using visual mode yank
- Replaces the
<
and>
characters with their HTML entity equivalents (<
and>
) - Positions the cursor at the end of the replaced tag
This should make it easy to escape HTML tags in your markdown files for Jekyll without having to edit weird control characters in your macros. The function works smoothly without requiring you to press Enter after invoking it.
Me: Excellent! Now hear is a much easier one. Claude (you) has this habit of using a syntax at the beginning of backticked Python blocks of code like this:
```python:pipulate/server.py
class Pipulate:
# ... existing code ...
And I have to change it into this because the Kramdown markdown editor in the Jekyll static site generator that GitHub Pages uses doesn’t like that. So I have to change it to this:
```python
# pipulate/server.py
class Pipulate:
# ... existing code ...
…and to do that, I use this macro (as copy/pasted from the reg:
command in
nvim:
c "a /python:^Mwi^M^[xi# ^[o^[
So you see, this is a request for a similar macro as the one you just made for me (which I moved to leader t, by the way). This one is much simpler because there’s no visual selection or buffer stuff. It’s just searching, deleting, hitting enter to knock something down a space. I’ll put this one on the p key (for python). I don’t need to generalize this one.
Please and thank you!
Claude: <think> The user wants a function similar to the previous one, but to transform code block headers from:
```python:pipulate/server.py
class Pipulate:
# ... existing code ...
```
to:
```python
# pipulate/server.py
class Pipulate:
# ... existing code ...
```
Their macro is:
/python:^Mwi^M^[xi# ^[o^[
Let me break this down:
/python:
- Search for “python:”^M
- Press Enter to jump to itw
- Move forward one word (to the colon)i
- Enter insert mode^M
- Press Enter to break the line^[
- Press Escape to exit insert modex
- Delete the coloni#
- Insert “# “ (the comment character and a space)^[
- Press Escape to exit insert modeo
- Open a new line below^[
- Press Escape to exit insert mode
I’ll create a function to perform this transformation and map it to <leader>p
.
</think>
Here’s a new function to handle the Python code block header transformation:
// ~/.config/nvim/init.lua
// ... existing code ...
-- Function to remove file path after code block language
function remove_code_filepath()
-- Get the current buffer
local bufnr = vim.api.nvim_get_current_buf()
-- Search for the pattern ```language:filepath pattern
local line_num = vim.fn.search('```[^:\n]*:', 'n')
if line_num == 0 then
vim.notify("No more code blocks with filepaths found", vim.log.levels.INFO)
return
end
-- Get the line content
local line = vim.api.nvim_buf_get_lines(bufnr, line_num - 1, line_num, false)[1]
-- Extract the language part (everything between ``` and :)
local language = line:match("```([^:\n]*)")
-- Extract the filepath part (everything after :)
local filepath = line:match(":([^\n]*)")
-- Replace the original line with just the language
vim.api.nvim_buf_set_lines(bufnr, line_num - 1, line_num, false, {"```" .. language})
-- Insert the filepath as a comment on the next line
vim.api.nvim_buf_set_lines(bufnr, line_num, line_num, false, {"# " .. filepath})
-- Move cursor to the next line after the comment
vim.api.nvim_win_set_cursor(0, {line_num + 2, 0})
vim.notify("Filepath removed from code block", vim.log.levels.INFO)
end
-- Add mapping for the new function (using <leader>p for "python")
vim.api.nvim_set_keymap('n', '<leader>p', '<cmd>lua remove_code_filepath()<CR>', { noremap = true, silent = true })
// ... existing code ...
This function:
- Finds the next code block with a filepath (```language:filepath)
- Extracts the language and filepath
- Replaces the original line with just ```language
- Adds the filepath as a comment (# filepath) on the next line
- Positions the cursor after the comment line
I used a more generalized name since it would work with any language, not just Python, but I’ve mapped it to <leader>p
as you requested.
Badaboom badabing, example number two of converting a vim macro into a nvim lua function!
Gemini Advanced 2.5 Experimental’s Take
Okay, here’s a summary from my perspective, along with title/headline ideas and my own take:
Summary (Author’s 1st Person Perspective):
[Moved to top]
Title & Headline Ideas:
- Titles:
- From VimScript Hell to Lua Heaven: AI-Assisted NeoVim Macros
- Why This Old-School Vim User Switched to NeoVim (Hint: It Wasn’t Plugins)
- Taming Vim Macros with NeoVim, Lua, and AI
- NeoVim: A Luddite’s Reluctant Upgrade for Better Macros
- Mastering Text: How AI Helped Me Refine NeoVim Macros
- The Unexpected Benefit of NeoVim: Easier Macro Editing
- Vim vs. NeoVim: An Old-Schooler’s Take on Macro Management
- Headlines:
- Sick of Editing Cryptic Vim Macros? See How NeoVim and AI Offer a Solution.
- I Ditched VimScript for Lua in NeoVim, and AI Made Macro Editing a Breeze.
- Even Vim Purists Might Find This NeoVim Feature Compelling: Easier Macros via Lua.
- How I Used Claude AI to Build a NeoVim Lua Macro for Escaping HTML Tags.
- Beyond Plugins: The Real Reason This Vim Veteran Embraced NeoVim.
- Unlock AI-Powered Macro Refinement: My Journey from Vim to NeoVim with Lua.
My (Gemini AI’s) Opinion of the Article:
This is a compelling and relatable article for anyone who has wrestled with the intricacies of Vim and its configuration. The author effectively uses a personal, practical problem—editing a specific Vim macro—as a narrative thread to explore broader themes: the trade-offs between Vim and NeoVim, the role of AI coding assistants, the value of foundational text-editing skills, and the sometimes necessary evolution of even deeply ingrained tech habits.
The step-by-step (though abbreviated) account of interacting with Claude to develop the Lua function provides a concrete example of AI-assisted development, including the realistic back-and-forth required for refinement. While the author acknowledges a tendency to ramble, the tangents about the lineage of text editors (ed
, vi
) and the universality of text in tech add a thoughtful, philosophical layer that elevates the piece beyond a simple technical tutorial. The first-person perspective makes the journey engaging, and the meta-narrative of using the developed macro on the article itself is a nice touch. It successfully argues that sometimes, adopting a slightly newer tool (NeoVim with Lua) doesn’t mean abandoning core principles (mastery of text manipulation) but can actually enhance them, especially with modern aids like AI.