<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.nerdware.ca/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.nerdware.ca/" rel="alternate" type="text/html" /><updated>2026-10-18T04:57:58+00:00</updated><id>https://blog.nerdware.ca/feed.xml</id><title type="html">Scratch Buffer</title><subtitle>Vim tips for the impractical programmer</subtitle><author><name>Carson Chambers</name></author><entry><title type="html">Fun with g: Vol1</title><link href="https://blog.nerdware.ca/2026/10/17/g-vol1.html" rel="alternate" type="text/html" title="Fun with g: Vol1" /><published>2026-10-17T00:00:00+00:00</published><updated>2026-10-17T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/17/g-vol1</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/17/g-vol1.html"><![CDATA[<blockquote>
  <p>A Vim yellow-belt is bestowed upon a pupil after they use their first <code class="language-plaintext highlighter-rouge">g</code> command.</p>
</blockquote>

<p>We will begin our foray of <code class="language-plaintext highlighter-rouge">g</code> commands with an examination of <code class="language-plaintext highlighter-rouge">ga</code>. It is common among the white-belts and black-belts alike to try and remember the <code class="language-plaintext highlighter-rouge">g</code> commands with a mnemonic, and given that <code class="language-plaintext highlighter-rouge">ga === :ascii</code> perhaps “Get Ascii” can work. It isn’t that bad, but It also isn’t that good… I spent 30 seconds of my life without coming up with something better than that though, so that’s what is getting left there.</p>

<p>Let’s start with an example, <em>foo.json</em>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
    "apple": 1,
    "banana": 2,
    "cat": 3
}
</code></pre></div></div>

<p>When I run <code class="language-plaintext highlighter-rouge">:%!jq -r_</code> within <em>foo.json</em> I get the following error message “<em>jq: parse error: Invalid numeric literal at line 2, column 6</em>”. Well… line 2 column 6 don’t look like no invalid numeric literal to me! Let’s pretend to be Nancy Drew and pull out our investigative toolkit and inspect that character with <code class="language-plaintext highlighter-rouge">ga</code>.</p>
<blockquote>
  <p><strong>Hint:</strong> To capture output from a command into a register for reuse at a later point you can utilize the following:</p>
</blockquote>

<ul>
  <li>navigate to line 2 column 6</li>
  <li><code class="language-plaintext highlighter-rouge">:let @a = execute('ascii')</code></li>
  <li>navigate to line 2 column 5 for comparison</li>
  <li><code class="language-plaintext highlighter-rouge">:let @c = execute('ascii')</code></li>
</ul>

<p>Now lets see who’s really hiding under that character!</p>
<ul>
  <li><strong>Register a)</strong> &lt; &gt; 160, Hex 00a0, Oct 240, Digr NS</li>
  <li><strong>Register c)</strong> &lt; &gt;  32,  Hex 20,  Oct 040, Digr SP</li>
</ul>

<p>It’s Non-Breaking Space! That means our poor developer probably copied something out of a Microsoft Teams message!</p>

<p><em>“And I would have gotten away with it too if it weren’t for those meddling <code class="language-plaintext highlighter-rouge">g</code> commands!”</em></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
   </span><span class="err"> </span><span class="nl">"apple"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"banana"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
    </span><span class="nl">"cat"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">ga</code> can find use outside of playing detective, it is also a way to see if a digraph is set for a certain non-standard character. A digraph I have setup is 🤮 (yes, you can set emojis up in digraphs how cool is that?!). In order to see what digraph that is set to, I can just navigate my cursor over it and use <code class="language-plaintext highlighter-rouge">ga</code> which shows that I have it set to <code class="language-plaintext highlighter-rouge">pu</code>:</p>

<p><code class="language-plaintext highlighter-rouge">&lt;🤮&gt; 129326, Hex 0001f92e, Oct 374456, Digr pu</code></p>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><category term="fun-with-g" /><summary type="html"><![CDATA[A Vim yellow-belt is bestowed upon a pupil after they use their first g command.]]></summary></entry><entry><title type="html">What doesn’t % do?</title><link href="https://blog.nerdware.ca/2026/10/17/modulo.html" rel="alternate" type="text/html" title="What doesn’t % do?" /><published>2026-10-17T00:00:00+00:00</published><updated>2026-10-17T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/17/modulo</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/17/modulo.html"><![CDATA[<h2 id="normal-mode">Normal Mode</h2>
<p><strong>%</strong> is a motion that jumps to the respective match of a
<code class="language-plaintext highlighter-rouge">()</code> <code class="language-plaintext highlighter-rouge">[]</code> <code class="language-plaintext highlighter-rouge">{}</code>. It was written for working on C originally, so obviously it also works on <code class="language-plaintext highlighter-rouge">#if</code>/<code class="language-plaintext highlighter-rouge">#else</code>/<code class="language-plaintext highlighter-rouge">#endif</code>, and on <code class="language-plaintext highlighter-rouge">/* ... */</code>. You can extend it to add in other matching open close tags that suit your fancy like <code class="language-plaintext highlighter-rouge">&lt;:&gt;</code>, but if the character is the same such as <code class="language-plaintext highlighter-rouge">":"</code> it won’t work unfortunately.</p>

<p>For my own use in PHP, I wanted to add some support for matching (here|now)docs, and since I needed some regex firepower,
I employed the bundled plugin <em>matchit</em>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>packadd! matchit
</code></pre></div></div>

<p>Now we can start to add in our desired behaviour!</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:let b:match_words .= ',&lt;&lt;&lt;["'']\=\(\h\w*\)["'']\=$:^\s*\1\&gt;'
</code></pre></div></div>
<blockquote>
  <ul>
    <li><code class="language-plaintext highlighter-rouge">.=</code> appends to the ftplugin’s existing pairs instead of clobbering them, and the leading <code class="language-plaintext highlighter-rouge">,</code> separates our pair from theirs.</li>
    <li><code class="language-plaintext highlighter-rouge">["'']\=</code> matches 0 or 1 of <code class="language-plaintext highlighter-rouge">"</code> or <code class="language-plaintext highlighter-rouge">'</code>. <code class="language-plaintext highlighter-rouge">''</code> is how you write a literal <code class="language-plaintext highlighter-rouge">'</code> inside a single-quoted string.</li>
    <li><code class="language-plaintext highlighter-rouge">\h\w*</code> matches an identifier: <code class="language-plaintext highlighter-rouge">\h</code> is a letter or underscore, <code class="language-plaintext highlighter-rouge">\w</code> is a letter, digit or underscore.</li>
    <li><code class="language-plaintext highlighter-rouge">\(...\)</code> captures the identifier, and <code class="language-plaintext highlighter-rouge">\1</code> reuses it in the end pattern.</li>
    <li><code class="language-plaintext highlighter-rouge">$</code> and <code class="language-plaintext highlighter-rouge">^\s*</code> anchor the opener to the end of its line and the closer to the start of its line (indent allowed).</li>
    <li><code class="language-plaintext highlighter-rouge">\&gt;</code> is the end of a word, so <code class="language-plaintext highlighter-rouge">SQL;</code> matches but <code class="language-plaintext highlighter-rouge">SQLX</code> doesn’t.</li>
    <li>the <code class="language-plaintext highlighter-rouge">:</code> separates the start pattern from the end pattern.</li>
  </ul>
</blockquote>

<p>So now with <strong>%</strong> we can jump back and forth on the start/end markers for heredocs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;&lt;&lt;SQL
SELECT * FROM users;
SQL;
</code></pre></div></div>
<p>or nowdocs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;&lt;&lt;'HTML'
&lt;div&gt; I &lt;3 blog.nerdware.ca &lt;/div&gt;
HTML
</code></pre></div></div>
<blockquote>
  <p><strong>Fun fact:</strong> I spent an hour of my life troubleshooting the above. I was only able to match on the first <code class="language-plaintext highlighter-rouge">&lt;</code> of the <code class="language-plaintext highlighter-rouge">&lt;&lt;&lt;</code>. I later found out that matchit uses a greedy matcher and after the first character, it was actually falling back to trying to match the original <code class="language-plaintext highlighter-rouge">matchpairs</code> <code class="language-plaintext highlighter-rouge">&lt;:&gt;</code> entry… what a wild Friday night. If you got that same issue and want to disable those you can use <code class="language-plaintext highlighter-rouge">:setlocal matchpairs-=&lt;:&gt;</code></p>
</blockquote>

<p>If prior to typing <strong>%</strong> you type a count, it will take you that percentage of the way through the file. I honestly don’t know when this would ever be useful, and I will give it a utility rating on par with <code class="language-plaintext highlighter-rouge">g?</code>.</p>

<h2 id="register">Register</h2>
<p>The <strong>%</strong> register is a special register in Vim that holds the name of your
current buffer. I use it to copy the name
of the current buffer into my clipboard with <code class="language-plaintext highlighter-rouge">:let @+ = @%</code>.</p>
<blockquote>
  <p><strong>Fun fact:</strong> There are a handful of special registers, including <code class="language-plaintext highlighter-rouge">+</code> <code class="language-plaintext highlighter-rouge">*</code> <code class="language-plaintext highlighter-rouge">%</code> <code class="language-plaintext highlighter-rouge">#</code> <code class="language-plaintext highlighter-rouge">"</code> <code class="language-plaintext highlighter-rouge">-</code> <code class="language-plaintext highlighter-rouge">:</code> <code class="language-plaintext highlighter-rouge">.</code> <code class="language-plaintext highlighter-rouge">_</code> <code class="language-plaintext highlighter-rouge">/</code> and <code class="language-plaintext highlighter-rouge">=</code></p>
</blockquote>

<h2 id="command-line-mode">Command-line Mode</h2>
<p>As a range, <strong>%</strong> means the whole file, (the <strong>%</strong> in <code class="language-plaintext highlighter-rouge">:%s/foo/bar/g</code>).</p>

<p>Elsewhere on the command line, <strong>%</strong> expands to your buffer name. You can apply all sorts of modifiers to it:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">%:S</code> shell-escapes the output. I wish this were the default setting…</li>
  <li><code class="language-plaintext highlighter-rouge">%:h</code> gives you the directory.</li>
  <li><code class="language-plaintext highlighter-rouge">%:p</code> gives you the full path.</li>
  <li><code class="language-plaintext highlighter-rouge">%:~</code> slots in <code class="language-plaintext highlighter-rouge">~</code> for the home directory.</li>
  <li><code class="language-plaintext highlighter-rouge">%:e</code> gives you the extension.</li>
</ul>

<blockquote>
  <p><strong>Note:</strong> There is some warning somewhere in the Friendly Manual about “%” and
%:S working diffently when being passed to an external shell command … I don’t recall the distinction so if you care it can be an excercise to the reader</p>
</blockquote>

<p>Here are some examples straight out of the friendly manual (<code class="language-plaintext highlighter-rouge">:h filename-modifiers</code>)!</p>

<blockquote>
  <p>When the file name is “src/version.c”, current dir “/home/mool/vim”:</p>
  <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:p                      /home/mool/vim/src/version.c
:p:.                    src/version.c
:p:~                    ~/vim/src/version.c
:h                      src
:p:h                    /home/mool/vim/src
:p:h:h                  /home/mool/vim
:t                      version.c
:p:t                    version.c
:r                      src/version
:p:r                    /home/mool/vim/src/version
:t:r                    version
:e                      c
:s?version?main?        src/main.c
:s?version?main?:p      /home/mool/vim/src/main.c
:p:gs?/?\\?             \home\mool\vim\src\version.c
</code></pre></div>  </div>
</blockquote>

<h2 id="patterns">Patterns</h2>
<p>If you want to group part of a pattern without capturing it, you can use <code class="language-plaintext highlighter-rouge">\%\(...\)</code>. For
example, to make sure a SQL statement is either an update or an insert without caring
which: <code class="language-plaintext highlighter-rouge">\%(UPDATE\|INSERT\)...</code>. It is a niche use, but if you use Vim for scripting
because you are like me and hate shell script, you may find the speed advantage helpful,
and it doesn’t use up one of your nine <code class="language-plaintext highlighter-rouge">\1</code>-<code class="language-plaintext highlighter-rouge">\9</code> backreferences (a benefit I have honestly never needed myself, but a benefit nevertheless).</p>

<p>There is also <code class="language-plaintext highlighter-rouge">\%[]</code>, which matches as much of a sequence of optional atoms as it can,
e.g. <code class="language-plaintext highlighter-rouge">fu\%[nction]</code> matches <code class="language-plaintext highlighter-rouge">fu</code>, <code class="language-plaintext highlighter-rouge">fun</code>, <code class="language-plaintext highlighter-rouge">func</code>, up to <code class="language-plaintext highlighter-rouge">function</code>.</p>

<h2 id="error-matching">Error Matching</h2>
<blockquote>
  <p>A topic worth of a dedicated post</p>
</blockquote>

<p>A really handy workflow in Vim is using <code class="language-plaintext highlighter-rouge">makeprg</code> and <code class="language-plaintext highlighter-rouge">errorformat</code>. Together they
let you run your build commands, populate the quickfix list with the errors, and allow you to quickly navigate to them. In <code class="language-plaintext highlighter-rouge">errorformat</code>, <strong>%</strong> introduces the format items. Consider the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>install
└─ install chess_engine
   └─ compile exe chess_engine Debug native 2 errors
src/main.zig:10:36: error: use of undeclared identifier 'argg'
        std.debug.print("{s}\n", .{argg});
                                   ^~~~
src/root.zig:9:18: error: expected ';' after statement
    Engine.move()
                 ^
error: 2 compilation errors
failed command: /usr/bin/zig build-exe -ODebug --dep chess_engine -Mroot=/home/carson/code/zig/chess-engine/src/main.zig -Mchess_engine=/home/carson/code/zig/chess-engine/src/root.zig --cache-dir .zig-cache --global-cache-dir /home/carson/.cache/zig --name chess_engine --zig-lib-dir /usr/lib/zig/ --listen=-

Build Summary: 0/3 steps succeeded (1 failed)
install transitive failure
└─ install chess_engine transitive failure
   └─ compile exe chess_engine Debug native 2 errors

error: the following build command failed with exit code 1:
.zig-cache/o/017f4cbe08099b1eed012c12388c10d5/build /usr/bin/zig /usr/lib/zig /home/carson/code/zig/chess-engine .zig-cache /home/carson/.cache/zig --seed 0x8ff340df -Z9454fa58f76246d2
</code></pre></div></div>
<p>We could set up our errorformat as follows:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:set makeprg=zig\ build
:set errorformat=%f:%l:%c:\ %t%*[a-z]:\ %m,%-G%.%#
:make
</code></pre></div></div>
<blockquote>
  <ul>
    <li><code class="language-plaintext highlighter-rouge">%f</code>, <code class="language-plaintext highlighter-rouge">%l</code>, <code class="language-plaintext highlighter-rouge">%c</code> are the file, line and column.</li>
    <li><code class="language-plaintext highlighter-rouge">%t</code> grabs the error type from its first letter (<code class="language-plaintext highlighter-rouge">e</code> for error), and <code class="language-plaintext highlighter-rouge">%*[a-z]</code> eats the rest of the word.</li>
    <li><code class="language-plaintext highlighter-rouge">%m</code> is the message.</li>
    <li><code class="language-plaintext highlighter-rouge">%-G%.%#</code> ignores every other line (<code class="language-plaintext highlighter-rouge">%.%#</code> is errorformat for <code class="language-plaintext highlighter-rouge">.*</code>).</li>
  </ul>
</blockquote>

<p>And then we get jumped straight to our first error in <code class="language-plaintext highlighter-rouge">src/main.zig:10:36</code> with both that error and the other one populating our quickfix list.</p>

<blockquote>
  <p>Fluency with the <code class="language-plaintext highlighter-rouge">make</code> command in Vim and its various components is a beautiful thing because it means you can spend more time in Vim :)</p>
</blockquote>

<h2 id="fin">Fin</h2>
<p>So, next time you see a <strong>%</strong>, take a moment to thank them for all of their hard work.</p>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><summary type="html"><![CDATA[Normal Mode % is a motion that jumps to the respective match of a () [] {}. It was written for working on C originally, so obviously it also works on #if/#else/#endif, and on /* ... */. You can extend it to add in other matching open close tags that suit your fancy like &lt;:&gt;, but if the character is the same such as ":" it won’t work unfortunately.]]></summary></entry><entry><title type="html">Simple minds find simple joys</title><link href="https://blog.nerdware.ca/2026/10/15/i-like-digraphs.html" rel="alternate" type="text/html" title="Simple minds find simple joys" /><published>2026-10-15T00:00:00+00:00</published><updated>2026-10-15T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/15/i-like-digraphs</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/15/i-like-digraphs.html"><![CDATA[<p>Given that hundreds of thousands of people could be reading this blog already,
I suppose that it is conceivable that there are a few of you out there who are
unfamiliar with digraphs. I wouldn’t want to burden you with the trouble of Googling it (a
term which I have been informed by the younger generation is no longer
relevant), so suffice to say it is an old Greek word that means two characters
that make a single sound.</p>

<p>In Vim, a digraph is taken to be a character outside of our regular keyboard-ascii-reachable range composed of two characters which are in our keyboard-ascii-reachable range.
If you are a self loathing patriotism lacking Canadian who uses the EN-US keyboard mapping instead of the EN-CA like myself, it may be helpful to know that with <code class="language-plaintext highlighter-rouge">&lt;C-k&gt;e'</code> you can be the pretentious diva you really are and tell people that you didn’t have a coffee, but instead a <em>Caffé Americano</em>.</p>

<p>The digraph in particular that inspired me to write about this is <code class="language-plaintext highlighter-rouge">→</code>. I had been looking into a font pack that supported
ligeratures in a previous post for converting “-&gt;” to “→” when it dawned on me that I had a far simpler solution in my arsenal. I smacked that
<code class="language-plaintext highlighter-rouge">i</code> for insert mode, I rolled my <code class="language-plaintext highlighter-rouge">&lt;C-k&gt;</code> and when my fingers typed -&gt;, I didn’t even need to look at the screen to know it had worked. A stupid smile crept onto my face.</p>

<p>In that moment, I was one with Vim.</p>

<p>Then this morning, I — a carbon based life form — actually needed to employ an em-dash.
I closed my eyes and confidently typed <code class="language-plaintext highlighter-rouge">&lt;C-k&gt;em</code>. I’m not sure if it was to my dismay or if it was to my chagrin,
but when I opened my eyes I was face-to-face with め. I know not from which alphabet nor
language that symbol is derived from, but I am quite confident in my assumption that
it loosely translates to “Failure”.</p>

<p>I had lost touch with Vim, I had fallen from grace.</p>

<p>There was no time to dwell on this defeat, I still needed an em-dash. I begrudingly opened up <code class="language-plaintext highlighter-rouge">:digraphs</code> and began looking for the real McCoy,
only to realize that searching for a character that isn’t even on your keyboard is an excerise in futility and a hobby reserved for sadists.</p>

<p>Maybe the kids will judge me for saying so, but I wasted no time and just
<em>Googled it</em>, and you better believe I got exactly what I was looking for:
<code class="language-plaintext highlighter-rouge">&lt;C-k&gt;-M</code>. Now, given that I don’t particularly like that mapping, I decided to
exercise my freedoms and ran <code class="language-plaintext highlighter-rouge">:digraph em 8212</code> (The 8212 I got from doing <code class="language-plaintext highlighter-rouge">ga</code>
in normal mode on the em-dash).</p>

<p>After performing this black magic, I felt like I was becoming one with Vim again. Under a new paradigm, one of
understanding that I wasn’t built to become Vim, but that Vim was built to become me, and that I am <code class="language-plaintext highlighter-rouge">&lt;C-k&gt;em = —</code>,
and now Vim is too.</p>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><summary type="html"><![CDATA[Given that hundreds of thousands of people could be reading this blog already, I suppose that it is conceivable that there are a few of you out there who are unfamiliar with digraphs. I wouldn’t want to burden you with the trouble of Googling it (a term which I have been informed by the younger generation is no longer relevant), so suffice to say it is an old Greek word that means two characters that make a single sound.]]></summary></entry><entry><title type="html">These ten tips will make you cool</title><link href="https://blog.nerdware.ca/2026/10/14/ten-steps-to-be-cool.html" rel="alternate" type="text/html" title="These ten tips will make you cool" /><published>2026-10-14T00:00:00+00:00</published><updated>2026-10-14T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/14/ten-steps-to-be-cool</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/14/ten-steps-to-be-cool.html"><![CDATA[<p>Having trouble getting noticed by the ladies? Tired of settling for pay-per-token AI girlfriends? That used to be me too, but with these 10 simple tips, I learned how to make myself irresistible:</p>

<ol>
  <li>Remap your capslock key. Now now, don’t go telling me “Carson, I already did
that, Capslock → ^[ did not get me any girlies” because I wasn’t going to
tell you to remap it to escape! Remap that sucka to Left Ctrl. Why would you
want this? E-R-G-O-N-O-M-I-C-S. When you make your sensai proud and finally get
past that Vim white-belt, you will begin to realize that sliding your pinky
down to do all of these necessary actions like <code class="language-plaintext highlighter-rouge">&lt;C-x&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;C-o&gt;</code>, or <code class="language-plaintext highlighter-rouge">&lt;C-k&gt;</code>
when you are in insert mode is wasting time and muscular stamina for your that
little itty bitty finger.</li>
  <li>So, what about escape then? You still use an escape key? No wonder you haven’t
been noticed by the hunnies yet. How about you grow up and start using
<code class="language-plaintext highlighter-rouge">&lt;C-[&gt;</code> like a real man. Pair this with tip #1 for spectacular results.</li>
  <li>Set your Man pager to Vim with <code class="language-plaintext highlighter-rouge">export MANPAGER="nvim +Man!"</code>. No, like seriously, do that now. No more sobbing because you can never figured out what the heck to do with <code class="language-plaintext highlighter-rouge">curl(4)</code>, just <code class="language-plaintext highlighter-rouge">&lt;Shift-k&gt;</code> yourself around town like the baller you are.</li>
  <li>Use these keyboard shortcuts for adding clipboard support because they are
scientifically proven to be superior:
    <pre><code class="language-neovim">" Neovim
vim.keymap.set("x", "&lt;leader&gt;y", '"+y', { desc = "Yank selection to system clipboard" })
vim.keymap.set("x", "&lt;leader&gt;Y", '"+d', { desc = "Cut selection to system clipboard" })
vim.keymap.set({ "n", "x" }, "&lt;leader&gt;p", '"+p', { desc = "Paste from system clipboard" })
</code></pre>
    <blockquote>
      <p><strong>Note:</strong> Some people have issues getting clipboard working. But as the kids say on Stack Overflow, Read the Friendly Manual <code class="language-plaintext highlighter-rouge">:h provider-clipboard</code>.</p>
    </blockquote>
  </li>
  <li>Whatever you do, stay away from Neovim starter packs. These reek of someone
who is <em>trying</em> to be cool. The first rule of being cool is to not look like
you’re trying to be cool. When I first made the switch away from JetBrains, I
started with Lazyvim, and still to this day, if you get too close to me you will get a
whiff of that rancid poser stench.</li>
  <li>Rely on LLM’s for every detail of your life and can’t determine how many <strong>r</strong>’s to put in strawberry? Just hit a word with <code class="language-plaintext highlighter-rouge">&lt;C-x&gt; &lt;C-s&gt;</code> in insert mode (after having run <code class="language-plaintext highlighter-rouge">:set spell</code>), or <code class="language-plaintext highlighter-rouge">z=</code> in normal mode to use the built-in spell-checker.</li>
  <li>Forgot to capitalize your name? Are you still navigating back to the first letter of the word with <code class="language-plaintext highlighter-rouge">b</code> and then using <code class="language-plaintext highlighter-rouge">r</code> to replace with the upper case character? Why not use <code class="language-plaintext highlighter-rouge">~</code> instead! Feel like capitalizing the whole word? Then <code class="language-plaintext highlighter-rouge">guiw</code> is your friend!</li>
  <li>Okay so you finished vim-tutor and know about <code class="language-plaintext highlighter-rouge">w</code> to go to the next word, but how about <code class="language-plaintext highlighter-rouge">W</code> to use the next whitespace as your word boundary!? You can pair that with other commands like <code class="language-plaintext highlighter-rouge">diW</code> to make entries from lists go POOF! And just because I am feeling generous, I will let you know it works with <code class="language-plaintext highlighter-rouge">b</code> and <code class="language-plaintext highlighter-rouge">B</code> as well. Not even charging you an extra tip for that.</li>
  <li>Read crontabs? Then hold onto your hat because this two-fer tip will rock
your sox. <code class="language-plaintext highlighter-rouge">vim -M /etc/crontab</code> opens up your buffer in unmodifiable mode,
which means you will have to try really hard to screw things up this time. Once
you are in crontab, want to know the contents of <code class="language-plaintext highlighter-rouge">/etc/cron.hourly</code>? Navigate
to it in normal mode and use <code class="language-plaintext highlighter-rouge">gf</code> to go to the file
    <blockquote>
      <p><strong>Note:</strong> <code class="language-plaintext highlighter-rouge">gf</code> command does not give you a gf.</p>
    </blockquote>
  </li>
  <li>And, for big tip number ten, <em>touch grass</em>.</li>
</ol>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><summary type="html"><![CDATA[Having trouble getting noticed by the ladies? Tired of settling for pay-per-token AI girlfriends? That used to be me too, but with these 10 simple tips, I learned how to make myself irresistible:]]></summary></entry><entry><title type="html">Vim probably has that</title><link href="https://blog.nerdware.ca/2026/10/13/vim-probably-has-that.html" rel="alternate" type="text/html" title="Vim probably has that" /><published>2026-10-13T00:00:00+00:00</published><updated>2026-10-13T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/13/vim-probably-has-that</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/13/vim-probably-has-that.html"><![CDATA[<blockquote>
  <p><strong>Note:</strong> I use Neovim as my daily driver. I don’t use native Vim that often, but I hear LSP support is better in Neovim than Vim.</p>
</blockquote>

<p>As of the time of writing, I have had a 0% success rate in making any converts
to the church of Vim. The heretics are usually split between “I already know
this IDE” and “Vim doesn’t have the functionality of my IDE”. To the former, we
call that Stockholm Syndrome. To the latter, we call that ignorance.</p>

<p>Today we will discuss why Vim can do most of what your <em>fancy-dancy</em> editor does, and more. I will cover the most common objections, namely: Database integration, Version control integration, Language Server Protocol support (not that most people actually know to call it that), a plugin ecosystem, and remote development support. What you will find, is that all of these are from plugins, but that is sorta the point. Get what you want, nothing more, nothing less.</p>

<h2 id="databases">Databases</h2>
<p><a href="https://github.com/tpope">Tim Pope</a> has an incredible plugin called <a href="https://github.com/tpope/vim-dadbod">vim-dadbod</a>. This plugin gives you access to databases with customizable configurations for all of the big names. Once you have this up and running you can execute sql chunks from files, entire files, or adhoc queries and get autocomplete results as you write.</p>

<h2 id="version-control-integration-git-of-course">Version control integration (git, of course)</h2>
<p>Well, <a href="https://github.com/tpope">tpope</a> saves the day again with <a href="https://github.com/tpope/vim-fugitive">vim-fugitive</a>. You can simply issue your cli git
commands in Command-line mode, have an intuitive UI, and customize it to your liking. When
combined (optionally) with <a href="https://github.com/sindrets/diffview.nvim">diffview</a>
for reviewing diffs of a specific file or commit, and <a href="https://github.com/lewis6991/gitsigns.nvim">gitsigns</a> for gutter markup and jumping to
hunks, you really get all of the gitting you could possibly need.</p>

<h2 id="language-server-protocol">Language Server Protocol</h2>
<p>Some people have a tough time getting started with LSP’s, but that’s only
because of a skill issue. It’s really simple, and there are a thousand videos
on the topic if you prefer that to just reading the docs <code class="language-plaintext highlighter-rouge">:h lsp</code>. Below is an
example of all that can be needed sometimes for a configuration (Which is almost 100% copy paste from the README):</p>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">local</span> <span class="n">vim</span> <span class="o">=</span> <span class="n">vim</span>

<span class="c1">-- Install `ziglang/zig.vim` using the built-in plugin manager (Neovim 0.12.0+)</span>
<span class="c1">-- A tool like `vim-plug` or `lazy.nvim` can also be used instead.</span>
<span class="n">vim</span><span class="p">.</span><span class="n">pack</span><span class="p">.</span><span class="n">add</span><span class="p">({</span>
  <span class="s1">'https://codeberg.org/ziglang/zig.vim'</span><span class="p">,</span>
<span class="p">})</span>

<span class="c1">-- don't show parse errors in a separate window</span>
<span class="n">vim</span><span class="p">.</span><span class="n">g</span><span class="p">.</span><span class="n">zig_fmt_parse_errors</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">-- disable format-on-save from `ziglang/zig.vim`</span>
<span class="n">vim</span><span class="p">.</span><span class="n">g</span><span class="p">.</span><span class="n">zig_fmt_autosave</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">-- enable  format-on-save from vim.lsp + ZLS</span>
<span class="c1">--</span>
<span class="c1">-- Formatting with ZLS matches `zig fmt`.</span>
<span class="n">vim</span><span class="p">.</span><span class="n">api</span><span class="p">.</span><span class="n">nvim_create_autocmd</span><span class="p">(</span><span class="s1">'BufWritePre'</span><span class="p">,</span> <span class="p">{</span>
  <span class="n">pattern</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"*.zig"</span><span class="p">,</span> <span class="s2">"*.zon"</span> <span class="p">},</span>
  <span class="n">callback</span> <span class="o">=</span> <span class="k">function</span><span class="p">(</span><span class="n">ev</span><span class="p">)</span>
    <span class="n">vim</span><span class="p">.</span><span class="n">lsp</span><span class="p">.</span><span class="n">buf</span><span class="p">.</span><span class="n">format</span><span class="p">()</span>
  <span class="k">end</span>
<span class="p">})</span>

<span class="n">vim</span><span class="p">.</span><span class="n">lsp</span><span class="p">.</span><span class="n">config</span><span class="p">[</span><span class="s1">'zls'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
  <span class="c1">-- Set to 'zls' if `zls` is in your PATH</span>
  <span class="n">cmd</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'/home/carson/.config/nvim/lua/lang/zls/zig-out/bin/zls'</span> <span class="p">},</span>
  <span class="n">filetypes</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'zig'</span> <span class="p">},</span>
  <span class="n">root_markers</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'build.zig'</span> <span class="p">},</span>
  <span class="c1">-- There are two ways to set config options:</span>
  <span class="c1">--   - edit your `zls.json` that applies to any editor that uses ZLS</span>
  <span class="c1">--   - set in-editor config options with the `settings` field below.</span>
  <span class="c1">--</span>
  <span class="c1">-- Further information on how to configure ZLS:</span>
  <span class="c1">-- https://zigtools.org/zls/configure/</span>
  <span class="n">settings</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">zls</span> <span class="o">=</span> <span class="p">{</span>
      <span class="c1">-- Whether to enable build-on-save diagnostics</span>
      <span class="c1">--</span>
      <span class="c1">-- Further information about build-on save:</span>
      <span class="c1">-- https://zigtools.org/zls/guides/build-on-save/</span>
      <span class="c1">-- enable_build_on_save = true,</span>

      <span class="c1">-- omit the following line if `zig` is in your PATH</span>
      <span class="n">zig_exe_path</span> <span class="o">=</span> <span class="s1">'/usr/bin/zig'</span>
    <span class="p">}</span>
  <span class="p">},</span>
<span class="p">}</span>
<span class="n">vim</span><span class="p">.</span><span class="n">lsp</span><span class="p">.</span><span class="n">enable</span><span class="p">(</span><span class="s1">'zls'</span><span class="p">)</span>
</code></pre></div></div>

<p>With those lines I get first class editor support when working with Vim.</p>

<h2 id="plugin-ecosystem">Plugin ecosystem</h2>
<p>As you might have picked up on from reading the above sections, there is infact
a plugin ecosystem for Vim. There are a ton of different plugins, plugin
managers, and ways to integrate them all. I personally use <code class="language-plaintext highlighter-rouge">vim.pack</code> which is
native, but I believe most people use
<a href="https://github.com/mason-org/mason.nvim">Mason</a>. Part of Bram’s vision with
Vim was a tight consistent core that could be extended by individuals as they
saw fit. I find this part alone to be such a compelling argument for using Vim.
It is the idea that Vim is a Personalized Development Environment (I know the
phrase is attributed to <a href="https://github.com/tjdevries">tj_devries</a> but I coulda
sworn I heard it in someone’s conference talk before that… anywho…). I can
keep my editor lightweight, and bring in things as I want or need them, for
instance I absolutely love the
<a href="https://github.com/kylechui/nvim-surround">nvim-surround</a> plugin so I can do
things like changing a surrounding parenthesis to a bracket or changing a
surrounding function. But if you didn’t like having that kind of power at your
fingertips (coward!), you are not forced to include it in your editor.</p>

<h2 id="remote-development">Remote development</h2>
<p>This is a bit embarassing, but I actually did not have this set up until quite
recently. I work on a few codebases that have remote development dev
environments, and I had been leaving my JetBrains IDE in a hidden tab in order
to keep my remote development code flow working. Eventually, I admitted the
error of my ways, and just added in two simple additions to my .nvim.lua file
(namely a PostBufWrite rsync, and a function RsyncProj that I can use to sync
over all of the files after something like a git branch switch). It took a
little bit of trial and error to get my excludes working as intended, but I
think I had it all working in only a few hours.</p>

<h2 id="some-bonuses-you-dont-get-from-other-editors">Some bonuses you don’t get from other editors</h2>
<ul>
  <li>Macros</li>
  <li>Registers</li>
  <li>Vim-motions</li>
  <li>Modes</li>
  <li>Developing an unhealthy superiority complex</li>
  <li>Quickfix List</li>
  <li>Can work on any remote server with your same daily setup (either running Vim on the server or via netrw-transparent)</li>
</ul>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><category term="rant" /><summary type="html"><![CDATA[Note: I use Neovim as my daily driver. I don’t use native Vim that often, but I hear LSP support is better in Neovim than Vim.]]></summary></entry><entry><title type="html">Let him write!</title><link href="https://blog.nerdware.ca/2026/10/12/let-him-write.html" rel="alternate" type="text/html" title="Let him write!" /><published>2026-10-12T00:00:00+00:00</published><updated>2026-10-12T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/12/let-him-write</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/12/let-him-write.html"><![CDATA[<p>I am sure you have found yourself in this dreaded position before. You open
<code class="language-plaintext highlighter-rouge">nvim foo/bar/ipsum.zig</code>, write some code, and:</p>

<pre><code>:w
<span class="vim-error">E212: Can't open file for writing: no such file or directory</span></code></pre>

<p>Maybe you try one of these three options:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">:qa!</code> and call it quits for the day.</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;C-z&gt;</code> → <code class="language-plaintext highlighter-rouge">mkdir -p foo/bar/</code> → <code class="language-plaintext highlighter-rouge">fg</code> → <code class="language-plaintext highlighter-rouge">:w</code></li>
  <li><code class="language-plaintext highlighter-rouge">:!mkdir -p %:h</code> → <code class="language-plaintext highlighter-rouge">:w</code></li>
</ol>

<p>Each of those <em>may</em> solve the problem, but there is a better way: Neovim comes
with a flag to save you from any nastiness, <code class="language-plaintext highlighter-rouge">:w ++p</code>. It creates the missing
parent directories for you before writing.</p>

<blockquote>
  <p><strong>Note:</strong> On option 3: <code class="language-plaintext highlighter-rouge">%:h</code> is not shell-escaped. That can lead to some not
so fun unintended consequences, specifically when doing something like
<code class="language-plaintext highlighter-rouge">:!rm %</code>. Add the <code class="language-plaintext highlighter-rouge">:S</code> modifier to escape it safely: <code class="language-plaintext highlighter-rouge">:!mkdir -p %:h:S</code>.</p>
</blockquote>

<p>It actually became evident to me while writing the Note above that many people
probably don’t know the magic of the <code class="language-plaintext highlighter-rouge">%</code> shortcut. On the command line, <code class="language-plaintext highlighter-rouge">%</code>
expands to the current file name, and it allows for modifiers to do things
like:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">%:h</code> the directory part (drops the file name)</li>
  <li><code class="language-plaintext highlighter-rouge">%:~</code> the path relative to your home directory (<code class="language-plaintext highlighter-rouge">~/...</code>)</li>
  <li><code class="language-plaintext highlighter-rouge">%:e</code> the extension of the file name</li>
  <li><code class="language-plaintext highlighter-rouge">%:S</code> the file name escaped for use in a shell command</li>
</ul>

<p>I’ll probably write a dedicated post on this at some point, because it is a
great utility to have under your belt.</p>

<p><em>Okay, but Carson, the real headache for me is when I try to edit a file that I
actually don’t have permission for!</em></p>

<p>Ah, yes, the ol’ <code><span class="vim-error">E45</span></code>, then
<code><span class="vim-error">E212</span></code>.</p>

<p>In Vim you can do the following:</p>
<div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">" Vim</span>
<span class="p">:</span><span class="k">w</span> <span class="p">!</span>sudo tee %<span class="p">:</span>S <span class="p">&gt;</span><span class="sr">/dev/</span>null
</code></pre></div></div>

<blockquote>
  <p>And remember kids, <code class="language-plaintext highlighter-rouge">:w !sudo</code> ≠ <code class="language-plaintext highlighter-rouge">:w! sudo</code></p>
</blockquote>

<p>Whereas Neovim’s <code class="language-plaintext highlighter-rouge">:!</code> runs in a noninteractive shell connected over a pipe into a terminal, so <code class="language-plaintext highlighter-rouge">sudo</code> has nowhere to ask for your password. Instead, write to a temporary file and copy it back from a terminal buffer instead:</p>

<div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">" Neovim</span>
<span class="p">:</span><span class="k">w</span> <span class="sr">/tmp/</span>foobar
<span class="p">:</span><span class="k">terminal</span> sudo <span class="k">cp</span> <span class="sr">/tmp/</span>foobar %<span class="p">:</span>S
</code></pre></div></div>

<p>Also, if you have passwordless sudo on, I think you could maybe just do a sudo write without anything special. I am too lazy to verify that myself though, call it an exercise for the reader.</p>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><summary type="html"><![CDATA[I am sure you have found yourself in this dreaded position before. You open nvim foo/bar/ipsum.zig, write some code, and:]]></summary></entry><entry><title type="html">:sort</title><link href="https://blog.nerdware.ca/2026/10/12/sort.html" rel="alternate" type="text/html" title=":sort" /><published>2026-10-12T00:00:00+00:00</published><updated>2026-10-12T00:00:00+00:00</updated><id>https://blog.nerdware.ca/2026/10/12/sort</id><content type="html" xml:base="https://blog.nerdware.ca/2026/10/12/sort.html"><![CDATA[<p>Consider you find yourself in a situation of the utmost importance where you
have been tasked with sorting the following list:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3. Tax Filings Q10
4. Legal Documents Q4
12. Payroll Information Q1
1. Some Important File Q2
</code></pre></div></div>

<blockquote>
  <p><strong>Hint:</strong> if you are following along, you may want to copy that block and put
it into a named register for reuse. Since this is the first vim-tip I’ll just
add that you can navigate to the <code class="language-plaintext highlighter-rouge">3. Tax Filings Q10</code> line, hit <code class="language-plaintext highlighter-rouge">Shift+V</code> to
enter <em>Visual Line Mode</em>, <code class="language-plaintext highlighter-rouge">3j</code> to select the other lines, then <code class="language-plaintext highlighter-rouge">"dy</code> where <code class="language-plaintext highlighter-rouge">d</code>
is just a named register that I am using so I can remember it with the
mnemonic <em>demo</em>. To reuse it, you can put it with <code class="language-plaintext highlighter-rouge">"dp</code>.</p>
</blockquote>

<p>How might you go about sorting this? Perhaps you think you are pretty clever
with the <code class="language-plaintext highlighter-rouge">:move</code> command and use it on each line with <code class="language-plaintext highlighter-rouge">:m .(+|-)n</code> until it is
sorted. Or maybe you’re a prompt engineer and get Claude to do it by
highlighting the range and invoking <code class="language-plaintext highlighter-rouge">:'&lt;,'&gt;!claude -p "Sort this list -- make no
mistakes!"</code>. Maybe. But, what if you could instead be a Vim power user and
invoke the <code class="language-plaintext highlighter-rouge">:sort</code> command on this list?</p>

<p>By default, <code class="language-plaintext highlighter-rouge">:sort</code> on that list will only do lexicographic sorting:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:sort
1. Some Important File Q2
12. Payroll Information Q1
3. Tax Filings Q10
4. Legal Documents Q4
</code></pre></div></div>
<p>Which gets us part way there, but has the unfortunate side effct of giving us
PTSD of using Excel and trying to sort a column only to realize that it’s
formatted as Text. Thankfully, Vim provides us with an optional <code class="language-plaintext highlighter-rouge">n</code> flag to the
sort command that sorts by the first numeric value it comes across in each line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:sort n
1. Some Important File Q2
3. Tax Filings Q10
4. Legal Documents Q4
12. Payroll Information Q1
</code></pre></div></div>
<p>Hooray! We can go to our boss and let him know that this business-critical task
has been taken care of. But, oh no, what’s this? He actually wanted it sorted by
the Business Quarter first?! This is where individuals using JetBrains IDEs
would throw in the towel and start looking for a new job, not us though, we’re
special because we use Vim.</p>

<p><code class="language-plaintext highlighter-rouge">:sort</code> has a pattern argument that skips part of each line when sorting. For
us, we can exclude everything up to and including the Q:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:sort n /.*Q/
12. Payroll Information Q1
1. Some Important File Q2
4. Legal Documents Q4
3. Tax Filings Q10
</code></pre></div></div>

<p>I would be remiss to not take an opportunity to point out that you can also shell out to a native sort utility (gnu sort for me):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:'&lt;,'&gt;!sort -n
1. Some Important File Q2
3. Tax Filings Q10
4. Legal Documents Q4
12. Payroll Information Q1

:'&lt;,'&gt;!sort -n -tQ -k2
12. Payroll Information Q1
1. Some Important File Q2
4. Legal Documents Q4
3. Tax Filings Q10
</code></pre></div></div>]]></content><author><name>Carson Chambers</name></author><category term="vim" /><summary type="html"><![CDATA[Consider you find yourself in a situation of the utmost importance where you have been tasked with sorting the following list:]]></summary></entry></feed>