<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Stefan VanBuren · Blog Posts</title>
    <link>https://stefan.vanburen.xyz/blog/</link>
    <description>Blog Posts on stefan.vanburen.xyz</description>
    <language>en-us</language>
    
    
    <managingEditor>stefan@vanburen.xyz (Stefan VanBuren)</managingEditor>
    <webMaster>stefan@vanburen.xyz (Stefan VanBuren)</webMaster>
    <copyright>©️ 2026 Stefan VanBuren</copyright>
    
    
      <atom:link href="https://stefan.vanburen.xyz/blog/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>cells - a language server for CEL</title>
      <link>https://stefan.vanburen.xyz/blog/cells/</link>
      <pubDate>Sun, 22 Feb 2026 19:24:55 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/cells/</guid>
      <description>&lt;p&gt;This weekend, I built a &lt;a href=&#34;https://microsoft.github.io/language-server-protocol/&#34;&gt;language server&lt;/a&gt; for &lt;a href=&#34;https://cel.dev&#34;&gt;CEL&lt;/a&gt; called &lt;a href=&#34;https://github.com/stefanvanburen/cells&#34;&gt;&lt;code&gt;cells&lt;/code&gt;&lt;/a&gt;.
It currently supports semantic highlighting, diagnostics, documentation on hover, signature help, references, document highlighting (for variable references), completion, and formatting.
CEL is a pretty simple language,
so a language server for it doesn&amp;rsquo;t really need to support things like go to definition or some of the other niceties that language servers typically support.
It only works on a single &lt;code&gt;.cel&lt;/code&gt; file at a time,
which is a filetype currently supported only in neovim nightly but &lt;a href=&#34;https://github.com/neovim/neovim/pull/37834&#34;&gt;will be supported in neovim 0.12&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Building this out, I brought over some of the ideas that I had integrated when I was building out some of the &lt;a href=&#34;https://buf.build/docs/cli/editors-lsp/&#34;&gt;Buf LSP server&lt;/a&gt; at &lt;code&gt;$WORK&lt;/code&gt;,
like that tests should have separate testdata files so that they could be independently verified by opening up an LSP-powered editor and taking a look at the files themselves.
On top of this, making the tests mostly table-based made it really simple for additional test cases to be added.&lt;/p&gt;
&lt;p&gt;Of course, building this in a weekend took working with AI (using &lt;a href=&#34;https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent&#34;&gt;&lt;code&gt;pi&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://ampcode.com/&#34;&gt;&lt;code&gt;amp&lt;/code&gt;&lt;/a&gt;).
One thing I had in mind was trying out the other LSP &amp;ldquo;frameworks&amp;rdquo; in the go ecosystem;
unfortunately, most seem to be pretty unmaintained these days.
&lt;a href=&#34;https://github.com/go-language-server&#34;&gt;&lt;code&gt;go.lsp.dev&lt;/code&gt;&lt;/a&gt; progress has &lt;a href=&#34;https://github.com/go-language-server/protocol/pull/52&#34;&gt;largely stalled&lt;/a&gt;,
and &lt;a href=&#34;https://github.com/tliron/glsp&#34;&gt;tliron/glsp&lt;/a&gt; was the only other major contender,
which seemed to be in a similar state.&lt;/p&gt;
&lt;p&gt;At the end of the day, the agent suggested that we ought to just borrow what charmbracelet had down with &lt;a href=&#34;https://github.com/charmbracelet/x/tree/main/powernap/pkg/lsp/protocol&#34;&gt;&lt;code&gt;powernap&lt;/code&gt;&lt;/a&gt;,
and vendor in the &lt;code&gt;gopls&lt;/code&gt; protocol code while maintaining the license,
and then &amp;ldquo;write&amp;rdquo; our own &lt;a href=&#34;https://www.jsonrpc.org/specification&#34;&gt;JSONRPC&lt;/a&gt; server,
based on the &lt;a href=&#34;https://github.com/sourcegraph/jsonrpc2&#34;&gt;sourcegraph implementation&lt;/a&gt;,
but without the legacy cruft.
I may end up upstreaming some of this approach to &lt;code&gt;buf lsp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure how many other LSP features &lt;code&gt;cells&lt;/code&gt; needs to be considered &amp;ldquo;complete&amp;rdquo;,
although I&amp;rsquo;ll continue to track CEL language features (as it&amp;rsquo;s still in a pre-1.0 state as a language),
and I&amp;rsquo;d like to add most of the LSP features as functionality in the CLI,
similar to &lt;a href=&#34;https://go.dev/gopls/command-line&#34;&gt;&lt;code&gt;gopls&lt;/code&gt;&amp;rsquo; CLI interface&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cells&lt;/code&gt; turned out to be a pretty perfect weekend project for working with agents:
LSP is &lt;a href=&#34;https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/&#34;&gt;fairly well-specified&lt;/a&gt;,
with existing patterns,
a tight feedback loop,
and a great &lt;a href=&#34;https://github.com/google/cel-go&#34;&gt;reference library in &lt;code&gt;cel-go&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fun times to be building things. 👷‍♂️&lt;/p&gt;
</description>
      <source:markdown><p>This weekend, I built a <a href="https://microsoft.github.io/language-server-protocol/">language server</a> for <a href="https://cel.dev">CEL</a> called <a href="https://github.com/stefanvanburen/cells"><code>cells</code></a>.
It currently supports semantic highlighting, diagnostics, documentation on hover, signature help, references, document highlighting (for variable references), completion, and formatting.
CEL is a pretty simple language,
so a language server for it doesn&rsquo;t really need to support things like go to definition or some of the other niceties that language servers typically support.
It only works on a single <code>.cel</code> file at a time,
which is a filetype currently supported only in neovim nightly but <a href="https://github.com/neovim/neovim/pull/37834">will be supported in neovim 0.12</a>.</p>
<p>Building this out, I brought over some of the ideas that I had integrated when I was building out some of the <a href="https://buf.build/docs/cli/editors-lsp/">Buf LSP server</a> at <code>$WORK</code>,
like that tests should have separate testdata files so that they could be independently verified by opening up an LSP-powered editor and taking a look at the files themselves.
On top of this, making the tests mostly table-based made it really simple for additional test cases to be added.</p>
<p>Of course, building this in a weekend took working with AI (using <a href="https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent"><code>pi</code></a> and <a href="https://ampcode.com/"><code>amp</code></a>).
One thing I had in mind was trying out the other LSP &ldquo;frameworks&rdquo; in the go ecosystem;
unfortunately, most seem to be pretty unmaintained these days.
<a href="https://github.com/go-language-server"><code>go.lsp.dev</code></a> progress has <a href="https://github.com/go-language-server/protocol/pull/52">largely stalled</a>,
and <a href="https://github.com/tliron/glsp">tliron/glsp</a> was the only other major contender,
which seemed to be in a similar state.</p>
<p>At the end of the day, the agent suggested that we ought to just borrow what charmbracelet had down with <a href="https://github.com/charmbracelet/x/tree/main/powernap/pkg/lsp/protocol"><code>powernap</code></a>,
and vendor in the <code>gopls</code> protocol code while maintaining the license,
and then &ldquo;write&rdquo; our own <a href="https://www.jsonrpc.org/specification">JSONRPC</a> server,
based on the <a href="https://github.com/sourcegraph/jsonrpc2">sourcegraph implementation</a>,
but without the legacy cruft.
I may end up upstreaming some of this approach to <code>buf lsp</code>.</p>
<p>I&rsquo;m not sure how many other LSP features <code>cells</code> needs to be considered &ldquo;complete&rdquo;,
although I&rsquo;ll continue to track CEL language features (as it&rsquo;s still in a pre-1.0 state as a language),
and I&rsquo;d like to add most of the LSP features as functionality in the CLI,
similar to <a href="https://go.dev/gopls/command-line"><code>gopls</code>&rsquo; CLI interface</a>.</p>
<p><code>cells</code> turned out to be a pretty perfect weekend project for working with agents:
LSP is <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/">fairly well-specified</a>,
with existing patterns,
a tight feedback loop,
and a great <a href="https://github.com/google/cel-go">reference library in <code>cel-go</code></a>.</p>
<p>Fun times to be building things. 👷‍♂️</p>
</source:markdown>
    </item>
    
    <item>
      <title>Fog of War</title>
      <link>https://stefan.vanburen.xyz/blog/fog-of-war/</link>
      <pubDate>Sat, 01 Nov 2025 19:45:36 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/fog-of-war/</guid>
      <description>&lt;p&gt;Growing up playing World of Warcraft (WoW)&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, I became familiar with the concept of
&lt;a href=&#34;https://breezewiki.com/wowpedia/wiki/Fog_of_War&#34;&gt;&amp;ldquo;Fog of War&amp;rdquo;&lt;/a&gt; — that is, areas of the world that your character hadn&amp;rsquo;t visited.
Once you moved close enough to a certain area on the map, the fog would &amp;ldquo;lift&amp;rdquo;, and that
area on your map would be filled in moving forward.
Also, you&amp;rsquo;d gain experience points for clearing Fog of War — at lower levels of the
game, you could level-up fairly quickly just by exploring new areas.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll give my brother credit — he was the first to see this as an opportunity for
exploration. He piloted his Troll Rogue, Zuljitan, on a journey across the continent of
Kalimdor after reaching level 10, the level at which Rogues in those days learned their
&amp;ldquo;Sprint&amp;rdquo; ability.
(We figured we&amp;rsquo;d need this to escape the various mobs that would attack
your character from long distance, being tens of levels above our characters.
Rogues also could &amp;ldquo;stealth&amp;rdquo;, i.e. become invisible at the cost of movement speed, but
mobs at such a large level differential could see through stealth.)&lt;/p&gt;
&lt;p&gt;Setting off south from the Crossroads in the Barrens, Zuljitan traipsed through the
Thousand Needles down to Tanaris to visit Gadgetzan, I believe he then made his way
through Un&amp;rsquo;Goro Crater to Silithus, then over the mountains to Feralas before ultimately
dying for the umpteenth time in Desolace and hearthstone-ing back $HOME.&lt;/p&gt;
&lt;p&gt;Although I originally played Horde (a Tauren Warrior), I eventually became drawn to the
Gnomes of Ironforge, and ended up on my own journey as the Gnome Rogue, Phread.
I liked that Gnomes had an advantage in the Engineering profession, and thought it was
neat that I could build goggles for my character at a low level, when most headgear in
the game was at that time for levels 40+ (of the original level-cap at 60).&lt;/p&gt;
&lt;p&gt;The plan was hatched: this time, on Azeroth, I&amp;rsquo;d need to make my way across the sea to
Gadgetzan to get the schematics I was looking for.
Phread&amp;rsquo;s journey was a little easier: taking the Deeprun Tram from Ironforge to
Stormwind, he headed south through Elwynn Forest, Westfall and Duskwood down to the far
south of Stranglethorn Vale&amp;rsquo;s Booty Bay, where he could catch the ship to Steamwheedle
Port and continue onwards to Gadgetzan.
I bought my schematic (I think?), and hearthstone&amp;rsquo;d $HOME.&lt;/p&gt;
&lt;figure&gt;
  &lt;img
    src=&#34;https://ids.lib.harvard.edu/ids/view/17386628&#34;
    alt=&#34;Homosassa Jungle by Winslow Homer&#34;
  /&gt;
  &lt;figcaption&gt;
    &lt;a href=&#34;https://harvardartmuseums.org/collections/object/306835&#34;&gt;
      Not quite Stranglethorn Vale
    &lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;p&gt;I&amp;rsquo;m now about 20 years removed from my WoW-playing days (wow!), but I&amp;rsquo;ve managed to find
a new way to clear Fog of War: &lt;a href=&#34;https://citystrides.com&#34;&gt;CityStrides&lt;/a&gt;. CityStrides is an app that lets you
keep track of the streets that you&amp;rsquo;ve run (or walked, or biked) in a city.
Once you&amp;rsquo;ve run an entire street (by completing all the &amp;ldquo;nodes&amp;rdquo; along it), it&amp;rsquo;s added to
your completed list.
Each city has a leaderboard of users ranked based on the percentage of streets they&amp;rsquo;ve
completed.&lt;/p&gt;
&lt;p&gt;I managed to make my way through 25% of Boston&amp;rsquo;s ~4,400 streets before we moved to
Ipswich in late January.
Ipswich has only 356 streets at the time of writing.
(The smallest &amp;ldquo;city&amp;rdquo; I&amp;rsquo;ve run, Sunfield, MI, has 18 streets — running 5 of them during a
5k over 10 years ago has resulted in me being the #1 (of 1) runner there on
CityStrides.) Before the move, I had nearly run 20% of Ipswich, mostly by running the
various roads out on Great Neck and Little Neck.
I made it my goal during paternity leave to finish as many streets in Ipswich as
possible (thanks to a loving, supportive wife and a relatively reasonably well-behaved
baby), hoping to possibly make it to 100% before my leave ended.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been meaning to write this for a couple months since paternity leave ended, but
hadn&amp;rsquo;t quite had the gumption to get this finished.
I haven&amp;rsquo;t finished the 100% I was hoping to get to: instead, I&amp;rsquo;ve been stuck at 80% for
most of the summer. The remaining streets are all 5+ miles away from my typical starting
point of our home, and are largely in areas that I&amp;rsquo;d classify as unfriendly to running.
I&amp;rsquo;m now slowly restarting my quest for 100% (VanBurens are box checkers, after all),
with an eye towards finishing by the end of the year.
I remain, for now, 2nd out of 506 on the &lt;a href=&#34;https://citystrides.com/cities/2253&#34;&gt;Ipswich CityStrides leaderboard&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img
    src=&#34;https://iiif.micr.io/uIpfV/full/%5E830,/0/default.webp&#34;
    alt=&#34;Tokubei running through a street by Kunimori&#34;
  /&gt;
  &lt;figcaption&gt;
    &lt;a href=&#34;https://www.rijksmuseum.nl/en/collection/object/Tokubei-running-through-a-street--7e5f432ba724707931197c90284c5867?tab=data&#34;&gt;
      Running the Streets
    &lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Still, it&amp;rsquo;s not about reaching 100%: it&amp;rsquo;s about understanding the area in which you&amp;rsquo;re
living at a deeper level, and looking closely at things (although, maybe not when cars
are screaming past you as you try to traipse down some of the unfriendly roads).
Clearing the Fog of War, if you will.&lt;/p&gt;
&lt;p&gt;🌄&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;We used to drive our parents crazy on weekends &amp;mdash;
we had a single shared WoW account,
so the first of us to wake up would scurry quietly downstairs to log on.
We each could play for an hour at a time before it was the others&amp;rsquo; turn,
so when the other eventually woke up,
we&amp;rsquo;d always say that we had &amp;ldquo;just started playing 5 minutes ago&amp;rdquo; to claim our full hour.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
      <source:markdown><p>Growing up playing World of Warcraft (WoW)<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, I became familiar with the concept of
<a href="https://breezewiki.com/wowpedia/wiki/Fog_of_War">&ldquo;Fog of War&rdquo;</a> — that is, areas of the world that your character hadn&rsquo;t visited.
Once you moved close enough to a certain area on the map, the fog would &ldquo;lift&rdquo;, and that
area on your map would be filled in moving forward.
Also, you&rsquo;d gain experience points for clearing Fog of War — at lower levels of the
game, you could level-up fairly quickly just by exploring new areas.</p>
<p>I&rsquo;ll give my brother credit — he was the first to see this as an opportunity for
exploration. He piloted his Troll Rogue, Zuljitan, on a journey across the continent of
Kalimdor after reaching level 10, the level at which Rogues in those days learned their
&ldquo;Sprint&rdquo; ability.
(We figured we&rsquo;d need this to escape the various mobs that would attack
your character from long distance, being tens of levels above our characters.
Rogues also could &ldquo;stealth&rdquo;, i.e. become invisible at the cost of movement speed, but
mobs at such a large level differential could see through stealth.)</p>
<p>Setting off south from the Crossroads in the Barrens, Zuljitan traipsed through the
Thousand Needles down to Tanaris to visit Gadgetzan, I believe he then made his way
through Un&rsquo;Goro Crater to Silithus, then over the mountains to Feralas before ultimately
dying for the umpteenth time in Desolace and hearthstone-ing back $HOME.</p>
<p>Although I originally played Horde (a Tauren Warrior), I eventually became drawn to the
Gnomes of Ironforge, and ended up on my own journey as the Gnome Rogue, Phread.
I liked that Gnomes had an advantage in the Engineering profession, and thought it was
neat that I could build goggles for my character at a low level, when most headgear in
the game was at that time for levels 40+ (of the original level-cap at 60).</p>
<p>The plan was hatched: this time, on Azeroth, I&rsquo;d need to make my way across the sea to
Gadgetzan to get the schematics I was looking for.
Phread&rsquo;s journey was a little easier: taking the Deeprun Tram from Ironforge to
Stormwind, he headed south through Elwynn Forest, Westfall and Duskwood down to the far
south of Stranglethorn Vale&rsquo;s Booty Bay, where he could catch the ship to Steamwheedle
Port and continue onwards to Gadgetzan.
I bought my schematic (I think?), and hearthstone&rsquo;d $HOME.</p>
<figure>
  <img
    src="https://ids.lib.harvard.edu/ids/view/17386628"
    alt="Homosassa Jungle by Winslow Homer"
  />
  <figcaption>
    <a href="https://harvardartmuseums.org/collections/object/306835">
      Not quite Stranglethorn Vale
    </a>
  </figcaption>
</figure>
<hr>
<p>I&rsquo;m now about 20 years removed from my WoW-playing days (wow!), but I&rsquo;ve managed to find
a new way to clear Fog of War: <a href="https://citystrides.com">CityStrides</a>. CityStrides is an app that lets you
keep track of the streets that you&rsquo;ve run (or walked, or biked) in a city.
Once you&rsquo;ve run an entire street (by completing all the &ldquo;nodes&rdquo; along it), it&rsquo;s added to
your completed list.
Each city has a leaderboard of users ranked based on the percentage of streets they&rsquo;ve
completed.</p>
<p>I managed to make my way through 25% of Boston&rsquo;s ~4,400 streets before we moved to
Ipswich in late January.
Ipswich has only 356 streets at the time of writing.
(The smallest &ldquo;city&rdquo; I&rsquo;ve run, Sunfield, MI, has 18 streets — running 5 of them during a
5k over 10 years ago has resulted in me being the #1 (of 1) runner there on
CityStrides.) Before the move, I had nearly run 20% of Ipswich, mostly by running the
various roads out on Great Neck and Little Neck.
I made it my goal during paternity leave to finish as many streets in Ipswich as
possible (thanks to a loving, supportive wife and a relatively reasonably well-behaved
baby), hoping to possibly make it to 100% before my leave ended.</p>
<p>I&rsquo;ve been meaning to write this for a couple months since paternity leave ended, but
hadn&rsquo;t quite had the gumption to get this finished.
I haven&rsquo;t finished the 100% I was hoping to get to: instead, I&rsquo;ve been stuck at 80% for
most of the summer. The remaining streets are all 5+ miles away from my typical starting
point of our home, and are largely in areas that I&rsquo;d classify as unfriendly to running.
I&rsquo;m now slowly restarting my quest for 100% (VanBurens are box checkers, after all),
with an eye towards finishing by the end of the year.
I remain, for now, 2nd out of 506 on the <a href="https://citystrides.com/cities/2253">Ipswich CityStrides leaderboard</a>.</p>
<figure>
  <img
    src="https://iiif.micr.io/uIpfV/full/%5E830,/0/default.webp"
    alt="Tokubei running through a street by Kunimori"
  />
  <figcaption>
    <a href="https://www.rijksmuseum.nl/en/collection/object/Tokubei-running-through-a-street--7e5f432ba724707931197c90284c5867?tab=data">
      Running the Streets
    </a>
  </figcaption>
</figure>
<p>Still, it&rsquo;s not about reaching 100%: it&rsquo;s about understanding the area in which you&rsquo;re
living at a deeper level, and looking closely at things (although, maybe not when cars
are screaming past you as you try to traipse down some of the unfriendly roads).
Clearing the Fog of War, if you will.</p>
<p>🌄</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>We used to drive our parents crazy on weekends &mdash;
we had a single shared WoW account,
so the first of us to wake up would scurry quietly downstairs to log on.
We each could play for an hour at a time before it was the others&rsquo; turn,
so when the other eventually woke up,
we&rsquo;d always say that we had &ldquo;just started playing 5 minutes ago&rdquo; to claim our full hour.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
</source:markdown>
    </item>
    
    <item>
      <title>Small Seasons</title>
      <link>https://stefan.vanburen.xyz/blog/small-seasons/</link>
      <pubDate>Sat, 23 Aug 2025 14:52:06 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/small-seasons/</guid>
      <description>&lt;p&gt;Since moving out to suburban Ipswich from Boston,
I’ve been on a “seasons” kick —
you know, those things that feel wonderful the first couple weeks they’re here and then eventually drag on until the next one comes along?
(Currently waiting patiently for Autumn to get started in New England!)&lt;/p&gt;
&lt;figure&gt;
  &lt;img
    src=&#34;https://nitter.net/pic/orig/media%2FFF-E7sJXoAwdFDz.jpg&#34;
    alt=&#34;Pondering my seasons&#34;
  /&gt;
  &lt;figcaption&gt;
    &lt;a href=&#34;https://nitter.net/simonsarris/status/1468038376280530947&#34;&gt;
      Pondering my seasons
    &lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;While I’m more thoroughly immersed in the seasons now than when I was in the city,
I still spend a considerable chunk of time computing.
Computing isn’t seasonal, unless you make it so.&lt;/p&gt;
&lt;p&gt;Since I came across &lt;a href=&#34;https://rosszurowski.com/&#34;&gt;Ross Zurowski&lt;/a&gt;’s &lt;a href=&#34;https://smallseasons.guide/&#34;&gt;smallseasons.guide&lt;/a&gt; (which details the Japanese concept of &lt;em&gt;sekki&lt;/em&gt;, or two-week-ish “seasons” — Ross goes into detail on this and other ponderings in &lt;a href=&#34;https://rosszurowski.com/log/2018/small-seasons-long-calendars&#34;&gt;“On Small Seasons and Long Calendars”&lt;/a&gt;), I’ve had the &lt;a href=&#34;https://gist.github.com/rosszurowski/c7132bf37f7344a775e262619f97ff18&#34;&gt;iCal&lt;/a&gt; humming along in the background, eager for a chance to put it to use.&lt;/p&gt;
&lt;p&gt;We’ve had our first hint of Autumn weather this last week, and I saw my opportunity.
Fall supposedly starts on September 21st around the equinox
(and then lasts until December 21st 🙄),
but everyone knows this is a hoax.
Autumn actually starts much earlier:
August 8th, based on the &lt;em&gt;sekki&lt;/em&gt;.
I &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/e4ff21cde49d887e9a44d2d75118bae0742bd7ce&#34;&gt;configured neovim to change between echanovski’s &lt;code&gt;mini{spring,summer,autumn,winter}&lt;/code&gt; colorschemes based on the change of the small seasons&lt;/a&gt;,
and similarly updated my site’s &lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/d20c5ab7590d6e5e8cd2717e4cd1a1a27e056b82/item/layouts/_partials/header.html#L12-50&#34;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;&lt;/a&gt;s
and &lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/d20c5ab7590d6e5e8cd2717e4cd1a1a27e056b82/item/assets/favicon-template.svg&#34;&gt;favicon&lt;/a&gt; to follow suit.&lt;/p&gt;
&lt;p&gt;Just a little thing to keep me grounded in the seasons,
despite the indoor setting,
along with our morning stroller walks!&lt;/p&gt;
&lt;p&gt;🍂&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;P.S.: Another way I&amp;rsquo;ve found to mind the seasons is to update my &lt;a href=&#34;https://pixfabrik.com/livingworlds/&#34;&gt;Living Worlds app&lt;/a&gt; on the first day of each month,
as a reminder of the passing time.&lt;/p&gt;
</description>
      <source:markdown><p>Since moving out to suburban Ipswich from Boston,
I’ve been on a “seasons” kick —
you know, those things that feel wonderful the first couple weeks they’re here and then eventually drag on until the next one comes along?
(Currently waiting patiently for Autumn to get started in New England!)</p>
<figure>
  <img
    src="https://nitter.net/pic/orig/media%2FFF-E7sJXoAwdFDz.jpg"
    alt="Pondering my seasons"
  />
  <figcaption>
    <a href="https://nitter.net/simonsarris/status/1468038376280530947">
      Pondering my seasons
    </a>
  </figcaption>
</figure>
<p>While I’m more thoroughly immersed in the seasons now than when I was in the city,
I still spend a considerable chunk of time computing.
Computing isn’t seasonal, unless you make it so.</p>
<p>Since I came across <a href="https://rosszurowski.com/">Ross Zurowski</a>’s <a href="https://smallseasons.guide/">smallseasons.guide</a> (which details the Japanese concept of <em>sekki</em>, or two-week-ish “seasons” — Ross goes into detail on this and other ponderings in <a href="https://rosszurowski.com/log/2018/small-seasons-long-calendars">“On Small Seasons and Long Calendars”</a>), I’ve had the <a href="https://gist.github.com/rosszurowski/c7132bf37f7344a775e262619f97ff18">iCal</a> humming along in the background, eager for a chance to put it to use.</p>
<p>We’ve had our first hint of Autumn weather this last week, and I saw my opportunity.
Fall supposedly starts on September 21st around the equinox
(and then lasts until December 21st 🙄),
but everyone knows this is a hoax.
Autumn actually starts much earlier:
August 8th, based on the <em>sekki</em>.
I <a href="https://github.com/stefanvanburen/dotfiles/commit/e4ff21cde49d887e9a44d2d75118bae0742bd7ce">configured neovim to change between echanovski’s <code>mini{spring,summer,autumn,winter}</code> colorschemes based on the change of the small seasons</a>,
and similarly updated my site’s <a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/d20c5ab7590d6e5e8cd2717e4cd1a1a27e056b82/item/layouts/_partials/header.html#L12-50"><code>&lt;h1&gt;</code></a>s
and <a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/d20c5ab7590d6e5e8cd2717e4cd1a1a27e056b82/item/assets/favicon-template.svg">favicon</a> to follow suit.</p>
<p>Just a little thing to keep me grounded in the seasons,
despite the indoor setting,
along with our morning stroller walks!</p>
<p>🍂</p>
<hr>
<p>P.S.: Another way I&rsquo;ve found to mind the seasons is to update my <a href="https://pixfabrik.com/livingworlds/">Living Worlds app</a> on the first day of each month,
as a reminder of the passing time.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Internet Phonebook</title>
      <link>https://stefan.vanburen.xyz/blog/internet-phonebook/</link>
      <pubDate>Mon, 07 Jul 2025 18:16:22 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/internet-phonebook/</guid>
      <description>&lt;p&gt;I was lucky enough to be included in the inaugural &lt;a href=&#34;https://internetphonebook.net&#34;&gt;Internet Phonebook&lt;/a&gt;, in the “Atmosphere” section on p. 22 — I’m number #58 if you’d like to &lt;a href=&#34;https://internetphonebook.net/#dial-a-site&#34;&gt;Dial-my-site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I ordered a copy to peruse for myself, and lo and behold, I had forgotten that I had submitted on a whim. Probably the first and last time a link to this little site of mine will appear in print. Still, glad to be included in, and support, this grand directory of the poetic web.&lt;/p&gt;
</description>
      <source:markdown><p>I was lucky enough to be included in the inaugural <a href="https://internetphonebook.net">Internet Phonebook</a>, in the “Atmosphere” section on p. 22 — I’m number #58 if you’d like to <a href="https://internetphonebook.net/#dial-a-site">Dial-my-site</a>.</p>
<p>I ordered a copy to peruse for myself, and lo and behold, I had forgotten that I had submitted on a whim. Probably the first and last time a link to this little site of mine will appear in print. Still, glad to be included in, and support, this grand directory of the poetic web.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Smart Speed</title>
      <link>https://stefan.vanburen.xyz/blog/smart-speed/</link>
      <pubDate>Sat, 30 Dec 2023 18:00:27 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/smart-speed/</guid>
      <description>&lt;p&gt;My &lt;a href=&#34;https://overcast.fm&#34;&gt;Overcast&lt;/a&gt; settings screen shows the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Smart Speed has saved you an extra 949 hours beyond speed adjustments alone.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&amp;rsquo;s a lot of time. I&amp;rsquo;ve been using Overcast to listen to podcasts since it launched, with years now of what-I&amp;rsquo;d-call &amp;ldquo;heavy&amp;rdquo; podcast listening.
I&amp;rsquo;ve spent most of that time listening on 1.5 or 2x speed,
with smart speed always turned on (after all, who can resist number-go-up).&lt;/p&gt;
&lt;p&gt;I recently read Keenan&amp;rsquo;s &lt;a href=&#34;https://gkeenan.co/avgb/hot-take-its-okay-if-we-dont-consume-all-of-the-worlds-information-before-we-die&#34;&gt;&amp;ldquo;Hot Take: It&amp;rsquo;s okay if we don&amp;rsquo;t consume all of the world&amp;rsquo;s information before we die&amp;rdquo;&lt;/a&gt;, and then subsequently &lt;em&gt;also&lt;/em&gt; listened to the audioblog version.
While listening, I turned off Smart Speed, and it really struck me the difference that I&amp;rsquo;d been missing all this time.
It&amp;rsquo;s not as though I didn&amp;rsquo;t intuitively know that the audio was sped up,
but there&amp;rsquo;s something different about listening to people talking to each other without pauses.&lt;/p&gt;
&lt;p&gt;For one, you might assume that the speakers are more intelligent:
they never need to consider their responses,
as they&amp;rsquo;re always immediately responding.
In a similar vein, there&amp;rsquo;s no context clues as to what one person said that made others on an episode &lt;em&gt;take time&lt;/em&gt; to consider -
which might be important than something that can be responded to immediately.
And of course, as Keenan mentions in his post,
for the producers that &lt;em&gt;do&lt;/em&gt; edit their audio,
using Smart Speed removes the potential to appreciate the pauses as they occur,
whether they&amp;rsquo;re within conversation or somewhere else.&lt;/p&gt;
&lt;p&gt;Back to Keenan&amp;rsquo;s audioblog, you can clearly hear this listening to the end of the podcast as he slows down for emphasis towards the end.&lt;/p&gt;
&lt;p&gt;All of this is much better articulated by Simon Sarris in his &lt;a href=&#34;https://map.simonsarris.com/p/audiobooks-are-books-and-theyre-also&#34;&gt;thoughts on audiobooks&lt;/a&gt;, where he notes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Listening is a skill &amp;hellip; Generally the pacing of nearly all media has quickened. Possibly the delivery of “more, faster” is the result of a too-great respect for novelty as an artistic flourish.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Clearly, none of this is groundbreaking -
but reconsidering using Smart Speed has been a nice change of pace.&lt;/p&gt;
&lt;p&gt;🐢&lt;/p&gt;
</description>
      <source:markdown><p>My <a href="https://overcast.fm">Overcast</a> settings screen shows the following:</p>
<blockquote>
<p>Smart Speed has saved you an extra 949 hours beyond speed adjustments alone.</p>
</blockquote>
<p>That&rsquo;s a lot of time. I&rsquo;ve been using Overcast to listen to podcasts since it launched, with years now of what-I&rsquo;d-call &ldquo;heavy&rdquo; podcast listening.
I&rsquo;ve spent most of that time listening on 1.5 or 2x speed,
with smart speed always turned on (after all, who can resist number-go-up).</p>
<p>I recently read Keenan&rsquo;s <a href="https://gkeenan.co/avgb/hot-take-its-okay-if-we-dont-consume-all-of-the-worlds-information-before-we-die">&ldquo;Hot Take: It&rsquo;s okay if we don&rsquo;t consume all of the world&rsquo;s information before we die&rdquo;</a>, and then subsequently <em>also</em> listened to the audioblog version.
While listening, I turned off Smart Speed, and it really struck me the difference that I&rsquo;d been missing all this time.
It&rsquo;s not as though I didn&rsquo;t intuitively know that the audio was sped up,
but there&rsquo;s something different about listening to people talking to each other without pauses.</p>
<p>For one, you might assume that the speakers are more intelligent:
they never need to consider their responses,
as they&rsquo;re always immediately responding.
In a similar vein, there&rsquo;s no context clues as to what one person said that made others on an episode <em>take time</em> to consider -
which might be important than something that can be responded to immediately.
And of course, as Keenan mentions in his post,
for the producers that <em>do</em> edit their audio,
using Smart Speed removes the potential to appreciate the pauses as they occur,
whether they&rsquo;re within conversation or somewhere else.</p>
<p>Back to Keenan&rsquo;s audioblog, you can clearly hear this listening to the end of the podcast as he slows down for emphasis towards the end.</p>
<p>All of this is much better articulated by Simon Sarris in his <a href="https://map.simonsarris.com/p/audiobooks-are-books-and-theyre-also">thoughts on audiobooks</a>, where he notes:</p>
<blockquote>
<p>Listening is a skill &hellip; Generally the pacing of nearly all media has quickened. Possibly the delivery of “more, faster” is the result of a too-great respect for novelty as an artistic flourish.</p>
</blockquote>
<p>Clearly, none of this is groundbreaking -
but reconsidering using Smart Speed has been a nice change of pace.</p>
<p>🐢</p>
</source:markdown>
    </item>
    
    <item>
      <title>App Defaults</title>
      <link>https://stefan.vanburen.xyz/blog/app-defaults/</link>
      <pubDate>Tue, 14 Nov 2023 18:17:01 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/app-defaults/</guid>
      <description>&lt;p&gt;Inspired by &lt;a href=&#34;https://defaults.rknight.me&#34;&gt;https://defaults.rknight.me&lt;/a&gt;, here are my current defaults:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mail Client: Mail.app&lt;/li&gt;
&lt;li&gt;Mail Server: &lt;a href=&#34;https://migadu.com&#34;&gt;Migadu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notes: &lt;a href=&#34;https://getdrafts.com&#34;&gt;Drafts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;To-Do: &lt;a href=&#34;https://culturedcode.com/things/&#34;&gt;Things&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;iPhone Photo Shooting: Camera.app&lt;/li&gt;
&lt;li&gt;Photo Management: Photos.app&lt;/li&gt;
&lt;li&gt;Calendar: Calendar.app&lt;/li&gt;
&lt;li&gt;Cloud File Storage: iCloud Drive&lt;/li&gt;
&lt;li&gt;RSS: &lt;a href=&#34;https://netnewswire.com&#34;&gt;NetNewsWire&lt;/a&gt; (client) + &lt;a href=&#34;https://feedbin.com&#34;&gt;Feedbin&lt;/a&gt; (server)&lt;/li&gt;
&lt;li&gt;Contacts: &lt;a href=&#34;https://flexibits.com/cardhop&#34;&gt;Cardhop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Browser: Safari&lt;/li&gt;
&lt;li&gt;Chat: Messages&lt;/li&gt;
&lt;li&gt;Bookmarks: &lt;a href=&#34;https://pinboard.in&#34;&gt;Pinboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Read It Later: &lt;a href=&#34;https://www.instapaper.com&#34;&gt;Instapaper&lt;/a&gt;; transitioning back from Reeder&amp;rsquo;s read-it-later&lt;/li&gt;
&lt;li&gt;Word Processing: n/a&lt;/li&gt;
&lt;li&gt;Spreadsheets: n/a&lt;/li&gt;
&lt;li&gt;Presentations: n/a&lt;/li&gt;
&lt;li&gt;Shopping Lists: Grocery&lt;/li&gt;
&lt;li&gt;Meal Planning: &lt;a href=&#34;https://getdrafts.com&#34;&gt;Drafts&lt;/a&gt; / &lt;a href=&#34;https://mela.recipes&#34;&gt;Mela&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Budgeting and Personal Finance: n/a&lt;/li&gt;
&lt;li&gt;News: Hacker News / lobste.rs / text.npr.org&lt;/li&gt;
&lt;li&gt;Music: Spotify / &lt;a href=&#34;https://brushedtype.co/doppler/&#34;&gt;Doppler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Podcasts: Overcast&lt;/li&gt;
&lt;li&gt;Password Management: 1Password&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also see my &lt;a href=&#34;https://stefan.vanburen.xyz/uses&#34;&gt;/uses&lt;/a&gt; page for other hardware / software!&lt;/p&gt;
</description>
      <source:markdown><p>Inspired by <a href="https://defaults.rknight.me">https://defaults.rknight.me</a>, here are my current defaults:</p>
<ul>
<li>Mail Client: Mail.app</li>
<li>Mail Server: <a href="https://migadu.com">Migadu</a></li>
<li>Notes: <a href="https://getdrafts.com">Drafts</a></li>
<li>To-Do: <a href="https://culturedcode.com/things/">Things</a></li>
<li>iPhone Photo Shooting: Camera.app</li>
<li>Photo Management: Photos.app</li>
<li>Calendar: Calendar.app</li>
<li>Cloud File Storage: iCloud Drive</li>
<li>RSS: <a href="https://netnewswire.com">NetNewsWire</a> (client) + <a href="https://feedbin.com">Feedbin</a> (server)</li>
<li>Contacts: <a href="https://flexibits.com/cardhop">Cardhop</a></li>
<li>Browser: Safari</li>
<li>Chat: Messages</li>
<li>Bookmarks: <a href="https://pinboard.in">Pinboard</a></li>
<li>Read It Later: <a href="https://www.instapaper.com">Instapaper</a>; transitioning back from Reeder&rsquo;s read-it-later</li>
<li>Word Processing: n/a</li>
<li>Spreadsheets: n/a</li>
<li>Presentations: n/a</li>
<li>Shopping Lists: Grocery</li>
<li>Meal Planning: <a href="https://getdrafts.com">Drafts</a> / <a href="https://mela.recipes">Mela</a></li>
<li>Budgeting and Personal Finance: n/a</li>
<li>News: Hacker News / lobste.rs / text.npr.org</li>
<li>Music: Spotify / <a href="https://brushedtype.co/doppler/">Doppler</a></li>
<li>Podcasts: Overcast</li>
<li>Password Management: 1Password</li>
</ul>
<p>Also see my <a href="/uses">/uses</a> page for other hardware / software!</p>
</source:markdown>
    </item>
    
    <item>
      <title>Style Guide</title>
      <link>https://stefan.vanburen.xyz/blog/style-guide/</link>
      <pubDate>Wed, 01 Feb 2023 19:48:30 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/style-guide/</guid>
      <description>&lt;p&gt;Inspired by Louie Mantia&amp;rsquo;s &lt;a href=&#34;https://lmnt.me/post/css.html&#34;&gt;CSS for This Blog&lt;/a&gt;,
I figured I&amp;rsquo;d publish the page I use locally for testing out how my CSS looks.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s over at &lt;a href=&#34;https://stefan.vanburen.xyz/style-guide&#34;&gt;/style-guide&lt;/a&gt; (&lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/main/item/content/style-guide.md&#34;&gt;source&lt;/a&gt;) and it&amp;rsquo;s basically a dump of the markdown tab on &lt;a href=&#34;https://www.bryanbraun.com/poor-mans-styleguide/&#34;&gt;bryanbraun.com/poor-mans-styleguide&lt;/a&gt;. A great, simple way to see the various styles for most everything on the site.&lt;/p&gt;
</description>
      <source:markdown><p>Inspired by Louie Mantia&rsquo;s <a href="https://lmnt.me/post/css.html">CSS for This Blog</a>,
I figured I&rsquo;d publish the page I use locally for testing out how my CSS looks.</p>
<p>It&rsquo;s over at <a href="/style-guide">/style-guide</a> (<a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/tree/main/item/content/style-guide.md">source</a>) and it&rsquo;s basically a dump of the markdown tab on <a href="https://www.bryanbraun.com/poor-mans-styleguide/">bryanbraun.com/poor-mans-styleguide</a>. A great, simple way to see the various styles for most everything on the site.</p>
</source:markdown>
    </item>
    
    <item>
      <title>November Review</title>
      <link>https://stefan.vanburen.xyz/blog/november-review/</link>
      <pubDate>Fri, 02 Dec 2022 12:08:30 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/november-review/</guid>
      <description>&lt;p&gt;A quick retrospective on my &lt;a href=&#34;https://stefan.vanburen.xyz/blog/november-goals&#34;&gt;November Goals&lt;/a&gt; post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Run 80 miles&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I fell a little short here, running just 58 miles for the month. But it was busier than I gave myself credit for: a trip to see friends and being on call for two straight weeks made things a bit tricky. I &lt;em&gt;should&lt;/em&gt; still be in good shape to get to 1000 miles for the year, which would likely be the most I&amp;rsquo;ve clocked in a year, ever.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No drinking during the week (except Thanksgiving!)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Mostly good! I might have had ~two days where I went out with friends during the week for a beer (or several), but overall was contained to weekends and Thanksgiving. Going to keep things going into December!&lt;/p&gt;
</description>
      <source:markdown><p>A quick retrospective on my <a href="/blog/november-goals">November Goals</a> post:</p>
<blockquote>
<p>Run 80 miles</p>
</blockquote>
<p>I fell a little short here, running just 58 miles for the month. But it was busier than I gave myself credit for: a trip to see friends and being on call for two straight weeks made things a bit tricky. I <em>should</em> still be in good shape to get to 1000 miles for the year, which would likely be the most I&rsquo;ve clocked in a year, ever.</p>
<blockquote>
<p>No drinking during the week (except Thanksgiving!)</p>
</blockquote>
<p>Mostly good! I might have had ~two days where I went out with friends during the week for a beer (or several), but overall was contained to weekends and Thanksgiving. Going to keep things going into December!</p>
</source:markdown>
    </item>
    
    <item>
      <title>Update 2022-11-03</title>
      <link>https://stefan.vanburen.xyz/blog/update-2022-11-03/</link>
      <pubDate>Thu, 03 Nov 2022 16:13:21 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/update-2022-11-03/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://stefan.vanburen.xyz/blog/grease-the-groove/&#34;&gt;I’ve been a little hesitant to write here, for whatever reason. Haven’t had the gumption to write, I suppose. So I’d like to start up a bit of a habit — call it a “reverse New Years resolution”: I’d like to write something here (either a blog or TIL) each day until the end of 2022.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;s/each day/each week&lt;/p&gt;
&lt;p&gt;A bit too much, I think, to post each day. But I&amp;rsquo;m glad to have started up the habit of thinking about what to write again, even if it&amp;rsquo;s inconsequential.&lt;/p&gt;
</description>
      <source:markdown><blockquote>
<p><a href="https://stefan.vanburen.xyz/blog/grease-the-groove/">I’ve been a little hesitant to write here, for whatever reason. Haven’t had the gumption to write, I suppose. So I’d like to start up a bit of a habit — call it a “reverse New Years resolution”: I’d like to write something here (either a blog or TIL) each day until the end of 2022.</a></p>
</blockquote>
<p>s/each day/each week</p>
<p>A bit too much, I think, to post each day. But I&rsquo;m glad to have started up the habit of thinking about what to write again, even if it&rsquo;s inconsequential.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Blair Witch</title>
      <link>https://stefan.vanburen.xyz/blog/blair-witch/</link>
      <pubDate>Mon, 31 Oct 2022 08:10:13 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/blair-witch/</guid>
      <description>&lt;p&gt;Watched &lt;a href=&#34;https://www.imdb.com/title/tt0185937/&#34;&gt;&amp;ldquo;The Blair Witch Project&amp;rdquo;&lt;/a&gt; last night as a bit of a pre-Halloween treat. Two thoughts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Some movies are just better in theaters.&lt;/li&gt;
&lt;li&gt;(Maybe related!): The bar for &amp;ldquo;scary&amp;rdquo; has gotten higher in the last 20 years.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Either way, worth the watch. Just don&amp;rsquo;t read the Wikipedia page first, and pay attention &amp;mdash; the found-film effect can be jarring.&lt;/p&gt;
</description>
      <source:markdown><p>Watched <a href="https://www.imdb.com/title/tt0185937/">&ldquo;The Blair Witch Project&rdquo;</a> last night as a bit of a pre-Halloween treat. Two thoughts:</p>
<ol>
<li>Some movies are just better in theaters.</li>
<li>(Maybe related!): The bar for &ldquo;scary&rdquo; has gotten higher in the last 20 years.</li>
</ol>
<p>Either way, worth the watch. Just don&rsquo;t read the Wikipedia page first, and pay attention &mdash; the found-film effect can be jarring.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Reading the Web</title>
      <link>https://stefan.vanburen.xyz/blog/reading-the-web/</link>
      <pubDate>Sat, 29 Oct 2022 09:48:31 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/reading-the-web/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve spent a decent amount of time tweaking my … &amp;ldquo;system&amp;rdquo; for reading articles and blogs on the &amp;rsquo;net that I figured I&amp;rsquo;d jot down what I&amp;rsquo;ve tried, and what&amp;rsquo;s currently working for me.&lt;/p&gt;
&lt;p&gt;I currently use two separate but related tools for reading the web: a feed reader, and a &amp;ldquo;read later&amp;rdquo; app. My first feed reader was &lt;a href=&#34;https://en.wikipedia.org/wiki/Google_Reader&#34;&gt;Google Reader&lt;/a&gt; until its untimely demise, followed by a long while of using &lt;a href=&#34;https://en.wikipedia.org/wiki/Newsbeuter&#34;&gt;newsbeuter&lt;/a&gt; to intermittently follow feeds. And for &amp;ldquo;read later&amp;rdquo;, I started with &lt;a href=&#34;https://www.instapaper.com&#34;&gt;Instapaper&lt;/a&gt;, close to the time it was initially released.&lt;/p&gt;
&lt;p&gt;Eventually, I stumbled on &lt;a href=&#34;https://feedbin.com&#34;&gt;Feedbin&lt;/a&gt;, which has hosted my feeds ever since. Feedbin is great for many reasons, including: muting feeds; the autogenerated email address for newsletters; the friendliness and &lt;a href=&#34;https://craigmod.com/essays/fast_software/&#34;&gt;speed&lt;/a&gt; of the interface. But I&amp;rsquo;ve never used it as a primary client for actually &lt;em&gt;reading&lt;/em&gt; my feeds — I&amp;rsquo;ve always preferred a native client over a web client where I can get one.&lt;/p&gt;
&lt;p&gt;And thus, to start with: &lt;a href=&#34;https://reeder.app&#34;&gt;Reeder.app&lt;/a&gt;. Reeder was my go-to feed reader since at least Reeder 3, and it paired nicely with Instapaper.&lt;/p&gt;
&lt;p&gt;I stuck with this setup for a long time. Eventually, though, I had ended up with too many saved articles in Instapaper. I tried splitting things out into folders to make &amp;ldquo;Home&amp;rdquo; more manageable; eventually, I settled on a &amp;ldquo;Long&amp;rdquo; folder that was for anything that Instapaper deemed to be over a &amp;ldquo;10 minute read&amp;rdquo;, and everything else was left in &amp;ldquo;Home&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I eventually declared bankruptcy on Instapaper, for a few reasons, not least of which involved Instapaper being primarily web-based (at the end, I could &lt;em&gt;feel&lt;/em&gt; the latency of talking to the server to interact with the app at all, despite Instapaper&amp;rsquo;s &amp;ldquo;native&amp;rdquo; apps).&lt;/p&gt;
&lt;p&gt;Next, I switched over to Reeder&amp;rsquo;s built-in &amp;ldquo;read later&amp;rdquo; feature, which let me send articles over to a more permanent archive, where they wouldn&amp;rsquo;t be cleared out after having been opened. This was great — using the same app for both &amp;ldquo;triaging&amp;rdquo; the feed and also for reading longer articles. It helped, too, that Reeder is a beautiful and thoughtfully designed app.&lt;/p&gt;
&lt;p&gt;But the reading experience wasn&amp;rsquo;t without bugs: for longer articles, if I switched away from the app while reading, it would very regularly lose my place in the article. And with the app being developed by a single person (as far as I know) with seemingly most of their focus on their new (similarly fantastic) recipe app &lt;a href=&#34;https://mela.recipes&#34;&gt;Mela&lt;/a&gt;, I wasn&amp;rsquo;t sure things would get fixed in the near future.&lt;/p&gt;
&lt;p&gt;After trying out a few alternative clients, I settled on &lt;a href=&#34;https://netnewswire.com&#34;&gt;NetNewsWire&lt;/a&gt; (NNW). NNW &lt;em&gt;feels&lt;/em&gt; native. And simple. It&amp;rsquo;s free and open source (still (reasonably) &lt;a href=&#34;https://github.com/Ranchero-Software/NetNewsWire/commits/main&#34;&gt;actively developed&lt;/a&gt;), and has apps for both macOS and iOS. Truly &lt;a href=&#34;https://daringfireball.net/linked/2020/03/20/mac-assed-mac-apps&#34;&gt;Mac-assed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;NNW doesn&amp;rsquo;t have a &amp;ldquo;read later&amp;rdquo; feature, but/and I&amp;rsquo;ve come to like that: I now only use Reeder as a &amp;ldquo;read later&amp;rdquo; app, and send articles to it from NNW. And funnily enough, the extra bit of friction of &lt;em&gt;not&lt;/em&gt; having a keyboard shortcut to send to Reeder&amp;rsquo;s &amp;ldquo;read later&amp;rdquo; (instead, needing to use NNW&amp;rsquo;s share menu) has kept me from adding too many articles, and makes me more considerate of what I add to my &amp;ldquo;read later&amp;rdquo; queue.&lt;/p&gt;
&lt;p&gt;Anyway — I wouldn&amp;rsquo;t call this setup perfect, but it&amp;rsquo;s been working out well for me over the past year or so since I&amp;rsquo;ve adopted it. Feedbin (feed sync and management) → NNW (client) → Reeder (read later).&lt;/p&gt;
&lt;p&gt;One important caveat in this whole setup: I only use the macOS version of NNW, even though there&amp;rsquo;s an iOS version, because I like to keep the number of &amp;ldquo;inboxes&amp;rdquo; and consumption opportunities on my phone to a minimum. I&amp;rsquo;ll talk about that more in a later post!&lt;/p&gt;
</description>
      <source:markdown><p>I&rsquo;ve spent a decent amount of time tweaking my … &ldquo;system&rdquo; for reading articles and blogs on the &rsquo;net that I figured I&rsquo;d jot down what I&rsquo;ve tried, and what&rsquo;s currently working for me.</p>
<p>I currently use two separate but related tools for reading the web: a feed reader, and a &ldquo;read later&rdquo; app. My first feed reader was <a href="https://en.wikipedia.org/wiki/Google_Reader">Google Reader</a> until its untimely demise, followed by a long while of using <a href="https://en.wikipedia.org/wiki/Newsbeuter">newsbeuter</a> to intermittently follow feeds. And for &ldquo;read later&rdquo;, I started with <a href="https://www.instapaper.com">Instapaper</a>, close to the time it was initially released.</p>
<p>Eventually, I stumbled on <a href="https://feedbin.com">Feedbin</a>, which has hosted my feeds ever since. Feedbin is great for many reasons, including: muting feeds; the autogenerated email address for newsletters; the friendliness and <a href="https://craigmod.com/essays/fast_software/">speed</a> of the interface. But I&rsquo;ve never used it as a primary client for actually <em>reading</em> my feeds — I&rsquo;ve always preferred a native client over a web client where I can get one.</p>
<p>And thus, to start with: <a href="https://reeder.app">Reeder.app</a>. Reeder was my go-to feed reader since at least Reeder 3, and it paired nicely with Instapaper.</p>
<p>I stuck with this setup for a long time. Eventually, though, I had ended up with too many saved articles in Instapaper. I tried splitting things out into folders to make &ldquo;Home&rdquo; more manageable; eventually, I settled on a &ldquo;Long&rdquo; folder that was for anything that Instapaper deemed to be over a &ldquo;10 minute read&rdquo;, and everything else was left in &ldquo;Home&rdquo;.</p>
<p>I eventually declared bankruptcy on Instapaper, for a few reasons, not least of which involved Instapaper being primarily web-based (at the end, I could <em>feel</em> the latency of talking to the server to interact with the app at all, despite Instapaper&rsquo;s &ldquo;native&rdquo; apps).</p>
<p>Next, I switched over to Reeder&rsquo;s built-in &ldquo;read later&rdquo; feature, which let me send articles over to a more permanent archive, where they wouldn&rsquo;t be cleared out after having been opened. This was great — using the same app for both &ldquo;triaging&rdquo; the feed and also for reading longer articles. It helped, too, that Reeder is a beautiful and thoughtfully designed app.</p>
<p>But the reading experience wasn&rsquo;t without bugs: for longer articles, if I switched away from the app while reading, it would very regularly lose my place in the article. And with the app being developed by a single person (as far as I know) with seemingly most of their focus on their new (similarly fantastic) recipe app <a href="https://mela.recipes">Mela</a>, I wasn&rsquo;t sure things would get fixed in the near future.</p>
<p>After trying out a few alternative clients, I settled on <a href="https://netnewswire.com">NetNewsWire</a> (NNW). NNW <em>feels</em> native. And simple. It&rsquo;s free and open source (still (reasonably) <a href="https://github.com/Ranchero-Software/NetNewsWire/commits/main">actively developed</a>), and has apps for both macOS and iOS. Truly <a href="https://daringfireball.net/linked/2020/03/20/mac-assed-mac-apps">Mac-assed</a>.</p>
<p>NNW doesn&rsquo;t have a &ldquo;read later&rdquo; feature, but/and I&rsquo;ve come to like that: I now only use Reeder as a &ldquo;read later&rdquo; app, and send articles to it from NNW. And funnily enough, the extra bit of friction of <em>not</em> having a keyboard shortcut to send to Reeder&rsquo;s &ldquo;read later&rdquo; (instead, needing to use NNW&rsquo;s share menu) has kept me from adding too many articles, and makes me more considerate of what I add to my &ldquo;read later&rdquo; queue.</p>
<p>Anyway — I wouldn&rsquo;t call this setup perfect, but it&rsquo;s been working out well for me over the past year or so since I&rsquo;ve adopted it. Feedbin (feed sync and management) → NNW (client) → Reeder (read later).</p>
<p>One important caveat in this whole setup: I only use the macOS version of NNW, even though there&rsquo;s an iOS version, because I like to keep the number of &ldquo;inboxes&rdquo; and consumption opportunities on my phone to a minimum. I&rsquo;ll talk about that more in a later post!</p>
</source:markdown>
    </item>
    
    <item>
      <title>November Goals</title>
      <link>https://stefan.vanburen.xyz/blog/november-goals/</link>
      <pubDate>Fri, 28 Oct 2022 20:00:30 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/november-goals/</guid>
      <description>&lt;p&gt;Setting some November goals a little early:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run 80 miles&lt;/p&gt;
&lt;p&gt;I set a goal of 500 miles this year — I&amp;rsquo;m already at 864 miles for the year, most of which I ran in the early summer. I&amp;rsquo;d really like to pass 1000 for the first time in a long while, and if I&amp;rsquo;m not at 950 by December, I can tell I&amp;rsquo;m not going to make it. 20 miles a week should be reasonable, given that I was getting up to 30-40 during the summer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;No drinking during the week (except Thanksgiving!)&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to drink a bit less — with the ramp up on running, and an on-call shift or two coming up, I&amp;rsquo;ll be avoiding drinking during the week, up until the holiday later on next month.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      <source:markdown><p>Setting some November goals a little early:</p>
<ol>
<li>
<p>Run 80 miles</p>
<p>I set a goal of 500 miles this year — I&rsquo;m already at 864 miles for the year, most of which I ran in the early summer. I&rsquo;d really like to pass 1000 for the first time in a long while, and if I&rsquo;m not at 950 by December, I can tell I&rsquo;m not going to make it. 20 miles a week should be reasonable, given that I was getting up to 30-40 during the summer.</p>
</li>
<li>
<p>No drinking during the week (except Thanksgiving!)</p>
<p>I&rsquo;d like to drink a bit less — with the ramp up on running, and an on-call shift or two coming up, I&rsquo;ll be avoiding drinking during the week, up until the holiday later on next month.</p>
</li>
</ol>
</source:markdown>
    </item>
    
    <item>
      <title>Grease the Groove</title>
      <link>https://stefan.vanburen.xyz/blog/grease-the-groove/</link>
      <pubDate>Thu, 27 Oct 2022 19:51:21 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/grease-the-groove/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been a little hesitant to write here, for whatever reason. Haven&amp;rsquo;t had the gumption to write, I suppose. So I&amp;rsquo;d like to start up a bit of a habit — call it a &amp;ldquo;reverse New Years resolution&amp;rdquo;: I&amp;rsquo;d like to write something here (either a blog or TIL) each day until the end of 2022.&lt;/p&gt;
&lt;p&gt;Grease the Groove, they say. Don&amp;rsquo;t worry about who&amp;rsquo;s reading (certainly not many!), and write like I&amp;rsquo;m writing to &lt;em&gt;one person&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll see how it goes.&lt;/p&gt;
</description>
      <source:markdown><p>I&rsquo;ve been a little hesitant to write here, for whatever reason. Haven&rsquo;t had the gumption to write, I suppose. So I&rsquo;d like to start up a bit of a habit — call it a &ldquo;reverse New Years resolution&rdquo;: I&rsquo;d like to write something here (either a blog or TIL) each day until the end of 2022.</p>
<p>Grease the Groove, they say. Don&rsquo;t worry about who&rsquo;s reading (certainly not many!), and write like I&rsquo;m writing to <em>one person</em>.</p>
<p>We&rsquo;ll see how it goes.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Seven GUIs</title>
      <link>https://stefan.vanburen.xyz/blog/seven-guis/</link>
      <pubDate>Fri, 10 Sep 2021 12:29:17 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/seven-guis/</guid>
      <description>&lt;p&gt;Recently I polished up and published my seven-guis project.
It’s an implementation of the &lt;a href=&#34;https://eugenkiss.github.io/7guis/&#34;&gt;7GUIs “GUI Programming Benchmark”&lt;/a&gt;, written in &lt;a href=&#34;https://clojurescript.org/&#34;&gt;ClojureScript&lt;/a&gt; and &lt;a href=&#34;https://github.com/reagent-project/reagent&#34;&gt;Reagent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It’s not quite complete — there are &lt;a href=&#34;https://eugenkiss.github.io/7guis/tasks&#34;&gt;7 tasks&lt;/a&gt;, and I finished all except for the last one (the spreadsheet — the most vaguely specified and open-ended, and the most difficult).
I’ll finish it down the road, but for now I&amp;rsquo;ve hosted it at &lt;a href=&#34;https://seven-guis.vanburen.xyz&#34;&gt;seven-guis.vanburen.xyz&lt;/a&gt;, and the source code is public, at &lt;a href=&#34;https://github.com/stefanvanburen/seven-guis&#34;&gt;github.com/stefanvanburen/seven-guis&lt;/a&gt;.&lt;/p&gt;
</description>
      <source:markdown><p>Recently I polished up and published my seven-guis project.
It’s an implementation of the <a href="https://eugenkiss.github.io/7guis/">7GUIs “GUI Programming Benchmark”</a>, written in <a href="https://clojurescript.org/">ClojureScript</a> and <a href="https://github.com/reagent-project/reagent">Reagent</a>.</p>
<p>It’s not quite complete — there are <a href="https://eugenkiss.github.io/7guis/tasks">7 tasks</a>, and I finished all except for the last one (the spreadsheet — the most vaguely specified and open-ended, and the most difficult).
I’ll finish it down the road, but for now I&rsquo;ve hosted it at <a href="https://seven-guis.vanburen.xyz">seven-guis.vanburen.xyz</a>, and the source code is public, at <a href="https://github.com/stefanvanburen/seven-guis">github.com/stefanvanburen/seven-guis</a>.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Uses</title>
      <link>https://stefan.vanburen.xyz/blog/uses/</link>
      <pubDate>Mon, 16 Aug 2021 21:28:11 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/uses/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve added a new &lt;a href=&#34;https://stefan.vanburen.xyz/uses&#34;&gt;uses&lt;/a&gt; page, detailing my personal computing setup.
I&amp;rsquo;m hoping to make it more prose-like in the future, but for now it lists my current most commonly used hardware and software.&lt;/p&gt;
</description>
      <source:markdown><p>I&rsquo;ve added a new <a href="/uses">uses</a> page, detailing my personal computing setup.
I&rsquo;m hoping to make it more prose-like in the future, but for now it lists my current most commonly used hardware and software.</p>
</source:markdown>
    </item>
    
    <item>
      <title>New Domain</title>
      <link>https://stefan.vanburen.xyz/blog/new-domain/</link>
      <pubDate>Fri, 16 Apr 2021 21:07:47 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/new-domain/</guid>
      <description>&lt;p&gt;A quick domain update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;svbn.me -&amp;gt; stefan.vanburen.xyz&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and to match, an email address update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;mailto:me@svbn.me&#34;&gt;me@svbn.me&lt;/a&gt; -&amp;gt; &lt;a href=&#34;mailto:stefan@vanburen.xyz&#34;&gt;stefan@vanburen.xyz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Will hold on to the old domain for awhile, so it should still work, but most things are being redirected.&lt;/p&gt;
</description>
      <source:markdown><p>A quick domain update:</p>
<ul>
<li>svbn.me -&gt; stefan.vanburen.xyz</li>
</ul>
<p>and to match, an email address update:</p>
<ul>
<li><a href="mailto:me@svbn.me">me@svbn.me</a> -&gt; <a href="mailto:stefan@vanburen.xyz">stefan@vanburen.xyz</a></li>
</ul>
<p>Will hold on to the old domain for awhile, so it should still work, but most things are being redirected.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Hiccup Makes Writing HTML Fun</title>
      <link>https://stefan.vanburen.xyz/blog/hiccup-makes-writing-html-fun/</link>
      <pubDate>Sun, 21 Mar 2021 19:48:36 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/hiccup-makes-writing-html-fun/</guid>
      <description>&lt;p&gt;At $work, I&amp;rsquo;ve been writing a web application that is server-side rendered via Go&amp;rsquo;s &lt;code&gt;html/template&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been writing a toy application in Clojure with a similar approach: server rendered HTML with &lt;a href=&#34;https://tailwindcss.com&#34;&gt;Tailwind&lt;/a&gt; for styling and &lt;a href=&#34;https://github.com/alpinejs/alpine&#34;&gt;AlpineJS&lt;/a&gt; for a hint of interactivity.&lt;/p&gt;
&lt;p&gt;With the introduction of Go&amp;rsquo;s new &lt;code&gt;embed&lt;/code&gt; package in 1.16, I was tempted to port the application to Go.
I was imagining being able to embed the templates and assets in a static binary.&lt;/p&gt;
&lt;p&gt;And I could still do that!
But I hesitated as soon as I reached the portion of rewriting my templates in Go, and for good reason: Go&amp;rsquo;s template syntax isn&amp;rsquo;t great, and Clojure has Hiccup.&lt;/p&gt;
&lt;h2 id=&#34;hiccup&#34;&gt;
  Hiccup &lt;a class=&#34;anchor&#34; href=&#34;#hiccup&#34;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/weavejester/hiccup&#34;&gt;Hiccup&lt;/a&gt; is a library for writing HTML in Clojure.
Writing HTML in Hiccup is a &lt;em&gt;dream&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unlike HTML, Hiccup automatically closes tags.&lt;/li&gt;
&lt;li&gt;Hiccup doesn&amp;rsquo;t require that you remember the proper way to close an HTML element.&lt;/li&gt;
&lt;li&gt;Hiccup has shortcuts for adding an &lt;code&gt;id&lt;/code&gt; or &lt;code&gt;class&lt;/code&gt; attributes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And, more than anything, Hiccup is written &lt;strong&gt;within&lt;/strong&gt; your regular Clojure functions, which makes it incredibly easy to interweave logic with your templates.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; in Hiccup might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:form&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:action&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/add-address&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;ss&#34;&gt;:method&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:label&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:for&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;label-input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:type&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:id&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;label-input&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;c1&#34;&gt;;; for now, label is required&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:required&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:label&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:for&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;street-input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Street&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:type&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:id&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;street-input&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;street&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:label&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:for&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;city-input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;City&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:type&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:id&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;city-input&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:label&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:for&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;state-input&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;State&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:input&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:type&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:id&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;state-input&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;ss&#34;&gt;:name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;;; …&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The beauty begins when you start to weave in the logic:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-clojure&#34; data-lang=&#34;clojure&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;def &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;disabled-classes&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;text-gray-500 …&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;defn &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;button&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;classes&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;disabled?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:button&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:id&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;ss&#34;&gt;:class&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;disabled?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                     &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;merge &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;classes&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;disabled-classes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                     &lt;span class=&#34;nv&#34;&gt;classes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;ss&#34;&gt;:disabled&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;disabled?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this way, Hiccup is similar to the JSX syntax while writing React — and this approach has been taken to the ClojureScript side via the &lt;a href=&#34;https://github.com/reagent-project/reagent&#34;&gt;Reagent&lt;/a&gt; library.
(A more thorough example of Reagent-style Hiccup code can be found in my &lt;a href=&#34;https://github.com/stefanvanburen/seven-guis/blob/main/src/app/main.cljs&#34;&gt;seven-guis&lt;/a&gt; project).&lt;/p&gt;
&lt;p&gt;And, when you pair Hiccup with &lt;a href=&#34;http://shaunlebron.github.io/parinfer/&#34;&gt;Parinfer&lt;/a&gt; + &lt;a href=&#34;https://github.com/Olical/conjure&#34;&gt;Conjure&lt;/a&gt;, the DX is too good to pass up.
While I love writing Go, the conciseness, simplicity and the REPL of Clojure have drawn me in.
It&amp;rsquo;s magical.&lt;/p&gt;
&lt;p&gt;🪄&lt;/p&gt;
</description>
      <source:markdown><p>At $work, I&rsquo;ve been writing a web application that is server-side rendered via Go&rsquo;s <code>html/template</code> package.</p>
<p>I&rsquo;ve been writing a toy application in Clojure with a similar approach: server rendered HTML with <a href="https://tailwindcss.com">Tailwind</a> for styling and <a href="https://github.com/alpinejs/alpine">AlpineJS</a> for a hint of interactivity.</p>
<p>With the introduction of Go&rsquo;s new <code>embed</code> package in 1.16, I was tempted to port the application to Go.
I was imagining being able to embed the templates and assets in a static binary.</p>
<p>And I could still do that!
But I hesitated as soon as I reached the portion of rewriting my templates in Go, and for good reason: Go&rsquo;s template syntax isn&rsquo;t great, and Clojure has Hiccup.</p>
<h2 id="hiccup">
  Hiccup <a class="anchor" href="#hiccup">#</a>
</h2>
<p><a href="https://github.com/weavejester/hiccup">Hiccup</a> is a library for writing HTML in Clojure.
Writing HTML in Hiccup is a <em>dream</em>:</p>
<ul>
<li>Unlike HTML, Hiccup automatically closes tags.</li>
<li>Hiccup doesn&rsquo;t require that you remember the proper way to close an HTML element.</li>
<li>Hiccup has shortcuts for adding an <code>id</code> or <code>class</code> attributes.</li>
</ul>
<p>And, more than anything, Hiccup is written <strong>within</strong> your regular Clojure functions, which makes it incredibly easy to interweave logic with your templates.</p>
<p>A <code>&lt;form&gt;</code> in Hiccup might look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">[</span><span class="ss">:form</span> <span class="p">{</span><span class="ss">:action</span> <span class="s">&#34;/add-address&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="ss">:method</span> <span class="s">&#34;POST&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:label</span> <span class="p">{</span><span class="ss">:for</span> <span class="s">&#34;label-input&#34;</span><span class="p">}</span> <span class="s">&#34;Label&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:id</span> <span class="s">&#34;label-input&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:name</span> <span class="s">&#34;label&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="c1">;; for now, label is required</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:required</span> <span class="nv">true</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:label</span> <span class="p">{</span><span class="ss">:for</span> <span class="s">&#34;street-input&#34;</span><span class="p">}</span> <span class="s">&#34;Street&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:id</span> <span class="s">&#34;street-input&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:name</span> <span class="s">&#34;street&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:label</span> <span class="p">{</span><span class="ss">:for</span> <span class="s">&#34;city-input&#34;</span><span class="p">}</span> <span class="s">&#34;City&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:id</span> <span class="s">&#34;city-input&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:name</span> <span class="s">&#34;city&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:label</span> <span class="p">{</span><span class="ss">:for</span> <span class="s">&#34;state-input&#34;</span><span class="p">}</span> <span class="s">&#34;State&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:id</span> <span class="s">&#34;state-input&#34;</span>
</span></span><span class="line"><span class="cl">           <span class="ss">:name</span> <span class="s">&#34;state&#34;</span><span class="p">}]]</span>
</span></span><span class="line"><span class="cl"><span class="c1">;; …</span>
</span></span></code></pre></div><p>The beauty begins when you start to weave in the logic:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-clojure" data-lang="clojure"><span class="line"><span class="cl"><span class="p">(</span><span class="k">def </span><span class="nv">disabled-classes</span> <span class="s">&#34;text-gray-500 …&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">defn </span><span class="nv">button</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="nv">id</span> <span class="nv">classes</span> <span class="nv">disabled?</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="ss">:button</span> <span class="p">{</span><span class="ss">:id</span> <span class="nv">id</span>
</span></span><span class="line"><span class="cl">            <span class="ss">:class</span> <span class="p">(</span><span class="k">if </span><span class="nv">disabled?</span>
</span></span><span class="line"><span class="cl">                     <span class="p">(</span><span class="nb">merge </span><span class="nv">classes</span> <span class="nv">disabled-classes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                     <span class="nv">classes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="ss">:disabled</span> <span class="nv">disabled?</span><span class="p">}])</span>
</span></span></code></pre></div><p>In this way, Hiccup is similar to the JSX syntax while writing React — and this approach has been taken to the ClojureScript side via the <a href="https://github.com/reagent-project/reagent">Reagent</a> library.
(A more thorough example of Reagent-style Hiccup code can be found in my <a href="https://github.com/stefanvanburen/seven-guis/blob/main/src/app/main.cljs">seven-guis</a> project).</p>
<p>And, when you pair Hiccup with <a href="http://shaunlebron.github.io/parinfer/">Parinfer</a> + <a href="https://github.com/Olical/conjure">Conjure</a>, the DX is too good to pass up.
While I love writing Go, the conciseness, simplicity and the REPL of Clojure have drawn me in.
It&rsquo;s magical.</p>
<p>🪄</p>
</source:markdown>
    </item>
    
    <item>
      <title>font-variant-numeric</title>
      <link>https://stefan.vanburen.xyz/blog/font-variant-numeric/</link>
      <pubDate>Sun, 24 Jan 2021 12:55:27 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/font-variant-numeric/</guid>
      <description>&lt;p&gt;I was reminded of the &lt;code&gt;font-variant-numeric&lt;/code&gt; CSS property by &lt;a href=&#34;https://twitter.com/jimniels/status/1353081335347351552&#34;&gt;Jim Nielsen, here&lt;/a&gt;, and ended up &lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz/commit/d6272469c9caf6b2286fa65cdef385f10e258642&#34;&gt;using it in a similar way&lt;/a&gt; for the dates on my &lt;a href=&#34;https://stefan.vanburen.xyz/blog&#34;&gt;list of blog posts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For context, without tabular-nums enabled, numbers on this site look like this:&lt;/p&gt;
&lt;p style=&#34;font-size: var(--size-700)&#34;&gt;
0123456789
&lt;/p&gt;
&lt;p&gt;And with it enabled, they look like this:&lt;/p&gt;
&lt;p style=&#34;font-size: var(--size-700)&#34; class=&#34;nums&#34;&gt;
0123456789
&lt;/p&gt;
&lt;p&gt;At least on Apple platforms, where the system sans-serif font is &lt;a href=&#34;https://developer.apple.com/fonts/&#34;&gt;San Francisco&lt;/a&gt;, the &amp;ldquo;0&amp;rdquo; and &amp;ldquo;1&amp;rdquo; characters have a little additional spacing, making them equal size to the rest of the digits.&lt;/p&gt;
&lt;p&gt;Glad to get this changed &amp;mdash; the previous look was somewhat off-putting, now that I look at it in comparison.
There are so many neat CSS properties nowadays for &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant&#34;&gt;all sorts of typographical situations&lt;/a&gt;.&lt;/p&gt;
</description>
      <source:markdown><p>I was reminded of the <code>font-variant-numeric</code> CSS property by <a href="https://twitter.com/jimniels/status/1353081335347351552">Jim Nielsen, here</a>, and ended up <a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/commit/d6272469c9caf6b2286fa65cdef385f10e258642">using it in a similar way</a> for the dates on my <a href="/blog">list of blog posts</a>.</p>
<p>For context, without tabular-nums enabled, numbers on this site look like this:</p>
<p style="font-size: var(--size-700)">
0123456789
</p>
<p>And with it enabled, they look like this:</p>
<p style="font-size: var(--size-700)" class="nums">
0123456789
</p>
<p>At least on Apple platforms, where the system sans-serif font is <a href="https://developer.apple.com/fonts/">San Francisco</a>, the &ldquo;0&rdquo; and &ldquo;1&rdquo; characters have a little additional spacing, making them equal size to the rest of the digits.</p>
<p>Glad to get this changed &mdash; the previous look was somewhat off-putting, now that I look at it in comparison.
There are so many neat CSS properties nowadays for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant">all sorts of typographical situations</a>.</p>
</source:markdown>
    </item>
    
    <item>
      <title>Misusing go&#39;s `fmt.Sscanf`</title>
      <link>https://stefan.vanburen.xyz/blog/misusing-go-fmt.sccanf/</link>
      <pubDate>Mon, 09 Nov 2020 08:12:09 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/misusing-go-fmt.sccanf/</guid>
      <description>&lt;p&gt;Recently I was building a new website in go at $work and needed to do some URL parsing to grab some expected parameters in the URL.
The URL was expected to look something like the following: &lt;code&gt;/path/:id1/:id2&lt;/code&gt;, where I was trying to grab &lt;code&gt;id1&lt;/code&gt; and &lt;code&gt;id2&lt;/code&gt; out of the URL.&lt;/p&gt;
&lt;p&gt;In the past, I&amp;rsquo;ve done something along the lines of:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;splitPath&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// error handling, etc.&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// splitPath is [&amp;#34;&amp;#34;, &amp;#34;path&amp;#34;, &amp;#34;:id1&amp;#34;, &amp;#34;:id2&amp;#34;],&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// because of the way `strings.Split` works&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;id1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;splitPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;id2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;splitPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, I had come across the following code, from &lt;a href=&#34;https://git.sr.ht/~sircmpwn/builds.sr.ht/tree/master/worker/http.go#L20&#34;&gt;the source code of builds.sr.ht&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;jobId&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;op&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sscanf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/job/%d/%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;jobId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;op&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;fmt.Sscanf&lt;/code&gt;? Haven&amp;rsquo;t seen that in use often! But it seemed like a great fit for the exact problem I was solving.&lt;/p&gt;
&lt;p&gt;So, I tried it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;matchCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sscanf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;URL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/path/%s/%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;err: %s, match count: %d\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;matchCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;id1: %s, id2: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;no err, id1: %s, id2: %s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s a &lt;a href=&#34;https://play.golang.org/p/I3AtQCF_Wvv&#34;&gt;slightly modified version on play.golang.org&lt;/a&gt;.&lt;/p&gt;
&lt;details&gt;
  &lt;summary&gt;Spoiler: here&#39;s the output.&lt;/summary&gt;
  &lt;samp&gt;
  err: unexpected EOF, match count: 1&lt;br&gt;
  id1: abc/def, id2:
  &lt;/samp&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;p&gt;So, the first match hit by the scan, because it&amp;rsquo;s &lt;code&gt;%s&lt;/code&gt;, continues through the &lt;code&gt;/&lt;/code&gt; character and picks up &lt;code&gt;id2&lt;/code&gt;&amp;rsquo;s value as well, leaving nothing for &lt;code&gt;id2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So why did it work in the line of code shown above?&lt;/p&gt;
&lt;p&gt;Because the &lt;code&gt;%d&lt;/code&gt; format specifier only captures numeric characters, so the &lt;code&gt;/&lt;/code&gt; character breaks up the scan.&lt;/p&gt;
&lt;p&gt;I messed around with this approach for awhile, but ultimately gave up and went back to my approach using &lt;code&gt;strings.Split&lt;/code&gt;.
But, it&amp;rsquo;s a nice reminder that format strings in go can be used both for formatting output and for scanning input, even if there are some footguns attached.&lt;/p&gt;
&lt;p&gt;🔫&lt;/p&gt;
</description>
      <source:markdown><p>Recently I was building a new website in go at $work and needed to do some URL parsing to grab some expected parameters in the URL.
The URL was expected to look something like the following: <code>/path/:id1/:id2</code>, where I was trying to grab <code>id1</code> and <code>id2</code> out of the URL.</p>
<p>In the past, I&rsquo;ve done something along the lines of:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">splitPath</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strings</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Path</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;/&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// error handling, etc.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// splitPath is [&#34;&#34;, &#34;path&#34;, &#34;:id1&#34;, &#34;:id2&#34;],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c1">// because of the way `strings.Split` works</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">id1</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">splitPath</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">id2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">splitPath</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="w">
</span></span></span></code></pre></div><p>However, I had come across the following code, from <a href="https://git.sr.ht/~sircmpwn/builds.sr.ht/tree/master/worker/http.go#L20">the source code of builds.sr.ht</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">jobId</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">op</span><span class="w">    </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sscanf</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Path</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;/job/%d/%s&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">jobId</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">op</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p><code>fmt.Sscanf</code>? Haven&rsquo;t seen that in use often! But it seemed like a great fit for the exact problem I was solving.</p>
<p>So, I tried it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">id1</span><span class="p">,</span><span class="w"> </span><span class="nx">id2</span><span class="w"> </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">matchCount</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Sscanf</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">Path</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;/path/%s/%s&#34;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">id1</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">id2</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;err: %s, match count: %d\n&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">,</span><span class="w"> </span><span class="nx">matchCount</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;id1: %s, id2: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">id1</span><span class="p">,</span><span class="w"> </span><span class="nx">id2</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;no err, id1: %s, id2: %s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">id1</span><span class="p">,</span><span class="w"> </span><span class="nx">id2</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Here&rsquo;s a <a href="https://play.golang.org/p/I3AtQCF_Wvv">slightly modified version on play.golang.org</a>.</p>
<details>
  <summary>Spoiler: here's the output.</summary>
  <samp>
  err: unexpected EOF, match count: 1<br>
  id1: abc/def, id2:
  </samp>
</details>
<hr>
<p>So, the first match hit by the scan, because it&rsquo;s <code>%s</code>, continues through the <code>/</code> character and picks up <code>id2</code>&rsquo;s value as well, leaving nothing for <code>id2</code>.</p>
<p>So why did it work in the line of code shown above?</p>
<p>Because the <code>%d</code> format specifier only captures numeric characters, so the <code>/</code> character breaks up the scan.</p>
<p>I messed around with this approach for awhile, but ultimately gave up and went back to my approach using <code>strings.Split</code>.
But, it&rsquo;s a nice reminder that format strings in go can be used both for formatting output and for scanning input, even if there are some footguns attached.</p>
<p>🔫</p>
</source:markdown>
    </item>
    
    <item>
      <title>rams.vim</title>
      <link>https://stefan.vanburen.xyz/blog/rams.vim/</link>
      <pubDate>Tue, 15 Sep 2020 16:38:18 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/rams.vim/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A quick update: I&amp;rsquo;ve &lt;a href=&#34;https://github.com/stefanvanburen/rams.vim/commit/7c7519489f552f37ca1f35011b220c38e0587103&#34;&gt;put the repository in maintenance mode&lt;/a&gt;,
and started work on a &lt;a href=&#34;https://github.com/stefanvanburen/rams&#34;&gt;neovim colorscheme&lt;/a&gt; based on the same colors.
The end goal is to have a generalized colorscheme that can be exported for use elsewhere.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I published the vim colorscheme I&amp;rsquo;ve been using for the last few months &lt;a href=&#34;https://github.com/stefanvanburen/rams.vim&#34;&gt;publicly on GitHub&lt;/a&gt;.
It&amp;rsquo;s called rams.vim, after the German Designer, &lt;a href=&#34;https://en.wikipedia.org/wiki/Dieter_Rams&#34;&gt;Dieter Rams&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had the idea for the colorscheme after watching &lt;a href=&#34;https://en.wikipedia.org/wiki/Gary_Hustwit&#34;&gt;Gary Hustwit&amp;rsquo;s&lt;/a&gt; &lt;a href=&#34;https://en.wikipedia.org/wiki/Rams_(2018_film)&#34;&gt;&lt;em&gt;Rams&lt;/em&gt;&lt;/a&gt;.
It&amp;rsquo;s simple: off-black, off-white, a grey and an accent color (a medium-bright red).
I also added a green and red that are specifically for showing diffs in vim.&lt;/p&gt;
&lt;p&gt;The colorscheme is generated via &lt;a href=&#34;https://github.com/lifepillar/vim-colortemplate&#34;&gt;vim-colortemplate&lt;/a&gt;, which aided the creation greatly &amp;mdash; I have no previous experience with vim colorscheme creation, and based on the other colorschemes I surveyed, it seemed like one of the few sane ways to get an idea up and running quickly.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m quite happy with the colorscheme as is &amp;mdash; I&amp;rsquo;ve been using the light colors for a couple months now.
The dark mode could use some work, but I&amp;rsquo;m not using it regularly, so I&amp;rsquo;ve been mostly keeping it in sync with the light variant.
I&amp;rsquo;ve also added some plugin-specific highlights for the obvious candidates (&lt;a href=&#34;https://github.com/dense-analysis/ale&#34;&gt;ALE&lt;/a&gt;, &lt;a href=&#34;https://github.com/tpope/vim-fugitive&#34;&gt;fugitive&lt;/a&gt; / &lt;a href=&#34;https://github.com/airblade/vim-gitgutter&#34;&gt;gitgutter&lt;/a&gt;), but haven&amp;rsquo;t explored all of the plugins I kept around after my &lt;a href=&#34;https://stefan.vanburen.xyz/blog/spring-cleaning&#34;&gt;dotfile purge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a vim user, give it a try, and let me know what you think!&lt;/p&gt;
</description>
      <source:markdown><blockquote>
<p>A quick update: I&rsquo;ve <a href="https://github.com/stefanvanburen/rams.vim/commit/7c7519489f552f37ca1f35011b220c38e0587103">put the repository in maintenance mode</a>,
and started work on a <a href="https://github.com/stefanvanburen/rams">neovim colorscheme</a> based on the same colors.
The end goal is to have a generalized colorscheme that can be exported for use elsewhere.</p>
</blockquote>
<p>I published the vim colorscheme I&rsquo;ve been using for the last few months <a href="https://github.com/stefanvanburen/rams.vim">publicly on GitHub</a>.
It&rsquo;s called rams.vim, after the German Designer, <a href="https://en.wikipedia.org/wiki/Dieter_Rams">Dieter Rams</a>.</p>
<p>I had the idea for the colorscheme after watching <a href="https://en.wikipedia.org/wiki/Gary_Hustwit">Gary Hustwit&rsquo;s</a> <a href="https://en.wikipedia.org/wiki/Rams_(2018_film)"><em>Rams</em></a>.
It&rsquo;s simple: off-black, off-white, a grey and an accent color (a medium-bright red).
I also added a green and red that are specifically for showing diffs in vim.</p>
<p>The colorscheme is generated via <a href="https://github.com/lifepillar/vim-colortemplate">vim-colortemplate</a>, which aided the creation greatly &mdash; I have no previous experience with vim colorscheme creation, and based on the other colorschemes I surveyed, it seemed like one of the few sane ways to get an idea up and running quickly.</p>
<p>I&rsquo;m quite happy with the colorscheme as is &mdash; I&rsquo;ve been using the light colors for a couple months now.
The dark mode could use some work, but I&rsquo;m not using it regularly, so I&rsquo;ve been mostly keeping it in sync with the light variant.
I&rsquo;ve also added some plugin-specific highlights for the obvious candidates (<a href="https://github.com/dense-analysis/ale">ALE</a>, <a href="https://github.com/tpope/vim-fugitive">fugitive</a> / <a href="https://github.com/airblade/vim-gitgutter">gitgutter</a>), but haven&rsquo;t explored all of the plugins I kept around after my <a href="/blog/spring-cleaning">dotfile purge</a>.</p>
<p>If you&rsquo;re a vim user, give it a try, and let me know what you think!</p>
</source:markdown>
    </item>
    
    <item>
      <title>small</title>
      <link>https://stefan.vanburen.xyz/blog/small/</link>
      <pubDate>Wed, 29 Jul 2020 19:24:11 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/small/</guid>
      <description>&lt;p&gt;I was browsing through my GitHub repositories the other day and noticed &lt;code&gt;small&lt;/code&gt;, a CLI tool I started working on about two years ago to convert text.
I finally polished up the repo and &lt;a href=&#34;https://github.com/stefanvanburen/small&#34;&gt;published it for all to see&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s really not much, but it gave me a chance to think about the expected behaviors associated with a tool like &lt;code&gt;small&lt;/code&gt;.
In particular, support for being piped (&lt;code&gt;$ command | small&lt;/code&gt;) is a critical bit of playing nice with other UNIX-y tools.&lt;/p&gt;
&lt;p&gt;Anyways, it was fun getting it out there.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; small -h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;USAGE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  small [FLAGS] [TEXT...]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  command | small [FLAGS]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;FLAGS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  -h         print this help message
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  -l         list transform types
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  -t=TYPE    specify transform type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; small &lt;span class=&#34;s2&#34;&gt;&amp;#34;here&amp;#39;s some text!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;ʜᴇʀᴇ&amp;#39;ꜱ ꜱᴏᴍᴇ ᴛᴇxᴛ!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;🤖&lt;/p&gt;
</description>
      <source:markdown><p>I was browsing through my GitHub repositories the other day and noticed <code>small</code>, a CLI tool I started working on about two years ago to convert text.
I finally polished up the repo and <a href="https://github.com/stefanvanburen/small">published it for all to see</a>.</p>
<p>It&rsquo;s really not much, but it gave me a chance to think about the expected behaviors associated with a tool like <code>small</code>.
In particular, support for being piped (<code>$ command | small</code>) is a critical bit of playing nice with other UNIX-y tools.</p>
<p>Anyways, it was fun getting it out there.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> small -h
</span></span><span class="line"><span class="cl"><span class="go">USAGE
</span></span></span><span class="line"><span class="cl"><span class="go">  small [FLAGS] [TEXT...]
</span></span></span><span class="line"><span class="cl"><span class="go">  command | small [FLAGS]
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">FLAGS
</span></span></span><span class="line"><span class="cl"><span class="go">  -h         print this help message
</span></span></span><span class="line"><span class="cl"><span class="go">  -l         list transform types
</span></span></span><span class="line"><span class="cl"><span class="go">  -t=TYPE    specify transform type
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="gp">$</span> small <span class="s2">&#34;here&#39;s some text!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="go">ʜᴇʀᴇ&#39;ꜱ ꜱᴏᴍᴇ ᴛᴇxᴛ!
</span></span></span></code></pre></div><p>🤖</p>
</source:markdown>
    </item>
    
    <item>
      <title>prutsen</title>
      <link>https://stefan.vanburen.xyz/blog/prutsen/</link>
      <pubDate>Thu, 11 Jun 2020 21:24:16 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/prutsen/</guid>
      <description>&lt;p&gt;I recently learned of &amp;ldquo;prutsen&amp;rdquo;, a Dutch word meaning &lt;a href=&#34;https://en.wiktionary.org/wiki/prutsen&#34;&gt;&amp;ldquo;to fiddle around&amp;rdquo;&lt;/a&gt;.
In a lot of ways, I think it encompasses a lot of how I approach some of my computer diversions.&lt;/p&gt;
&lt;p&gt;For example, this website, at the time of writing, has &lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz&#34;&gt;135 commits on it&lt;/a&gt;.
With a total of five published posts, and most of my posts being written within a commit or two, much of the work being done on this site is cosmetic.
For example, &lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz/commit/6107cf3e0cbea2313179880abab3fd3050693a12&#34;&gt;tweaking CSS indentation&lt;/a&gt; or &lt;a href=&#34;https://git.sr.ht/~svbn/stefan.vanburen.xyz/commit/12330139fc35f3d5289910bc9367b5abee8b5e12&#34;&gt;ensuring the RSS feed contains the entire content of the post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Likewise, my dotfiles sit at a &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles&#34;&gt;healthy 910 commits&lt;/a&gt; currently.
I recently went through a bit of a &lt;a href=&#34;https://stefan.vanburen.xyz/blog/spring-cleaning&#34;&gt;dotfile purge&lt;/a&gt; that added quite a few of those, but in general, tweaking my dotfiles has been a hobby for the better part of a decade.&lt;/p&gt;
&lt;p&gt;Prutsen gives me a name for all this tweaking and fiddling &amp;mdash; so cathartic!&lt;/p&gt;
&lt;p&gt;💆&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: After this post I truly could not remember where I had found &amp;ldquo;prutsen&amp;rdquo; in the first place.
I finally found it: &lt;a href=&#34;https://thedobook.co/products/do-inhabit-style-your-space-for-a-creative-and-considered-life&#34;&gt;Do Inhabit&lt;/a&gt;, by Sue Fan and Danielle Quigley.&lt;/p&gt;
&lt;p&gt;From the book:&lt;/p&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
    &lt;p&gt;
    This Dutch word translates loosely to doing something of very little significance that only looks like work, or tinkering.
    &lt;/p&gt;
  &lt;/blockquote&gt;
  &lt;figcaption&gt;
    Page 11, &lt;cite&gt;Do Inhabit&lt;/cite&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Good to know where it originally came from!&lt;/p&gt;
</description>
      <source:markdown><p>I recently learned of &ldquo;prutsen&rdquo;, a Dutch word meaning <a href="https://en.wiktionary.org/wiki/prutsen">&ldquo;to fiddle around&rdquo;</a>.
In a lot of ways, I think it encompasses a lot of how I approach some of my computer diversions.</p>
<p>For example, this website, at the time of writing, has <a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz">135 commits on it</a>.
With a total of five published posts, and most of my posts being written within a commit or two, much of the work being done on this site is cosmetic.
For example, <a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/commit/6107cf3e0cbea2313179880abab3fd3050693a12">tweaking CSS indentation</a> or <a href="https://git.sr.ht/~svbn/stefan.vanburen.xyz/commit/12330139fc35f3d5289910bc9367b5abee8b5e12">ensuring the RSS feed contains the entire content of the post</a>.</p>
<p>Likewise, my dotfiles sit at a <a href="https://github.com/stefanvanburen/dotfiles">healthy 910 commits</a> currently.
I recently went through a bit of a <a href="/blog/spring-cleaning">dotfile purge</a> that added quite a few of those, but in general, tweaking my dotfiles has been a hobby for the better part of a decade.</p>
<p>Prutsen gives me a name for all this tweaking and fiddling &mdash; so cathartic!</p>
<p>💆</p>
<p><em>Update</em>: After this post I truly could not remember where I had found &ldquo;prutsen&rdquo; in the first place.
I finally found it: <a href="https://thedobook.co/products/do-inhabit-style-your-space-for-a-creative-and-considered-life">Do Inhabit</a>, by Sue Fan and Danielle Quigley.</p>
<p>From the book:</p>
<figure>
  <blockquote>
    <p>
    This Dutch word translates loosely to doing something of very little significance that only looks like work, or tinkering.
    </p>
  </blockquote>
  <figcaption>
    Page 11, <cite>Do Inhabit</cite>
  </figcaption>
</figure>
<p>Good to know where it originally came from!</p>
</source:markdown>
    </item>
    
    <item>
      <title>git pull --autostash</title>
      <link>https://stefan.vanburen.xyz/blog/git-pull-autostash/</link>
      <pubDate>Wed, 13 May 2020 19:11:09 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/git-pull-autostash/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently moved from a largely merge commit based git workflow to a squash and merge based one.
Learning to flex my &lt;code&gt;git rebase&lt;/code&gt; muscles has been refreshing, and I&amp;rsquo;m enjoying the cleaner &lt;code&gt;git log&lt;/code&gt; that results.&lt;/p&gt;
&lt;p&gt;As part of the workflow, I&amp;rsquo;ve changed my &lt;code&gt;git pull&lt;/code&gt; to &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/de0f57867ba3270212c02884ec1053e64158fa1b&#34;&gt;default to rebasing on pull&lt;/a&gt;, rather than merging (the default).
The only thing that&amp;rsquo;s rough about this workflow is whenever I have local changes, &lt;code&gt;git pull&lt;/code&gt; will fail, telling me that I have unstaged changes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; git pull
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;error: cannot pull with rebase: You have unstaged changes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;error: please commit or stash them.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To work around this, I&amp;rsquo;d typically do a compound shell command (I&amp;rsquo;m using &lt;code&gt;fish&lt;/code&gt; &amp;mdash; you would typically do this with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; in &lt;code&gt;bash&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; git stash&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; and git pull&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; and git stash pop
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Saved working directory and index state WIP on main: 79911a7 chore: Remove gemini
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Already up to date.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;On branch main
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Your branch is up to date with &amp;#39;origin/main&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Changes not staged for commit:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  (use &amp;#34;git add &amp;lt;file&amp;gt;...&amp;#34; to update what will be committed)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;  (use &amp;#34;git restore &amp;lt;file&amp;gt;...&amp;#34; to discard changes in working directory)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        modified:   content/blog/git-pull-autostash.md
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;        modified:   content/blog/small.md
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;no changes added to commit (use &amp;#34;git add&amp;#34; and/or &amp;#34;git commit -a&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Dropped refs/stash@{0} (2ecce561c9bbd9cf222848d4b225a49edb816bb2)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I recently discovered the solution to needing this: &lt;code&gt;git pull --autostash&lt;/code&gt;!
It automatically stashes your current working directory and re-applies it after the pull.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-console&#34; data-lang=&#34;console&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gp&#34;&gt;$&lt;/span&gt; git pull --autostash
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Created autostash: a14af18
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Current branch master is up to date.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;go&#34;&gt;Applied autostash.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This option is so handy for my workflow that I &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/297733&#34;&gt;made an alias for it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;🥳&lt;/p&gt;
</description>
      <source:markdown><p>I&rsquo;ve recently moved from a largely merge commit based git workflow to a squash and merge based one.
Learning to flex my <code>git rebase</code> muscles has been refreshing, and I&rsquo;m enjoying the cleaner <code>git log</code> that results.</p>
<p>As part of the workflow, I&rsquo;ve changed my <code>git pull</code> to <a href="https://github.com/stefanvanburen/dotfiles/commit/de0f57867ba3270212c02884ec1053e64158fa1b">default to rebasing on pull</a>, rather than merging (the default).
The only thing that&rsquo;s rough about this workflow is whenever I have local changes, <code>git pull</code> will fail, telling me that I have unstaged changes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> git pull
</span></span><span class="line"><span class="cl"><span class="go">error: cannot pull with rebase: You have unstaged changes.
</span></span></span><span class="line"><span class="cl"><span class="go">error: please commit or stash them.
</span></span></span></code></pre></div><p>To work around this, I&rsquo;d typically do a compound shell command (I&rsquo;m using <code>fish</code> &mdash; you would typically do this with <code>&amp;&amp;</code> in <code>bash</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> git stash<span class="p">;</span> and git pull<span class="p">;</span> and git stash pop
</span></span><span class="line"><span class="cl"><span class="go">Saved working directory and index state WIP on main: 79911a7 chore: Remove gemini
</span></span></span><span class="line"><span class="cl"><span class="go">Already up to date.
</span></span></span><span class="line"><span class="cl"><span class="go">On branch main
</span></span></span><span class="line"><span class="cl"><span class="go">Your branch is up to date with &#39;origin/main&#39;.
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">Changes not staged for commit:
</span></span></span><span class="line"><span class="cl"><span class="go">  (use &#34;git add &lt;file&gt;...&#34; to update what will be committed)
</span></span></span><span class="line"><span class="cl"><span class="go">  (use &#34;git restore &lt;file&gt;...&#34; to discard changes in working directory)
</span></span></span><span class="line"><span class="cl"><span class="go">        modified:   content/blog/git-pull-autostash.md
</span></span></span><span class="line"><span class="cl"><span class="go">        modified:   content/blog/small.md
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">no changes added to commit (use &#34;git add&#34; and/or &#34;git commit -a&#34;)
</span></span></span><span class="line"><span class="cl"><span class="go">Dropped refs/stash@{0} (2ecce561c9bbd9cf222848d4b225a49edb816bb2)
</span></span></span></code></pre></div><p>I recently discovered the solution to needing this: <code>git pull --autostash</code>!
It automatically stashes your current working directory and re-applies it after the pull.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> git pull --autostash
</span></span><span class="line"><span class="cl"><span class="go">Created autostash: a14af18
</span></span></span><span class="line"><span class="cl"><span class="go">Current branch master is up to date.
</span></span></span><span class="line"><span class="cl"><span class="go">Applied autostash.
</span></span></span></code></pre></div><p>This option is so handy for my workflow that I <a href="https://github.com/stefanvanburen/dotfiles/commit/297733">made an alias for it</a>.</p>
<p>🥳</p>
</source:markdown>
    </item>
    
    <item>
      <title>Spring Cleaning</title>
      <link>https://stefan.vanburen.xyz/blog/spring-cleaning/</link>
      <pubDate>Sat, 18 Apr 2020 08:11:33 -0400</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/spring-cleaning/</guid>
      <description>&lt;p&gt;Spring is in the air!
Inspired by &lt;a href=&#34;https://deletionday.com/&#34;&gt;Deletion Day&lt;/a&gt;, I&amp;rsquo;ve been tidying up some of my physical and digital life.
I&amp;rsquo;ve deleted old accounts, removed applications that I&amp;rsquo;m not using, and gotten rid of extraneous hardware.
But, the cleanup I&amp;rsquo;m most proud of is my &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles&#34;&gt;dotfiles&lt;/a&gt; cleanse.&lt;/p&gt;
&lt;h2 id=&#34;the-purge&#34;&gt;
  the purge &lt;a class=&#34;anchor&#34; href=&#34;#the-purge&#34;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;For awhile, I was a bit of a dotfiles packrat.
I worried that the knowledge I had gained &amp;mdash; the choices I had made &amp;mdash; would be lost to the depths of time if I were to remove them from the text of the files themselves.
That meant old color schemes, old key bindings, old tips and tricks, all stayed in their respective places.
Perhaps moved around, but not deleted.
Over the years, this meant that my &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/blob/4d1f7f/vimrc&#34;&gt;vimrc had bloated up to ~1600 lines&lt;/a&gt;!
I had also accumulated a lot of files for tools I&amp;rsquo;m no longer using, that were strewn all over my system.&lt;/p&gt;
&lt;p&gt;I finally reached a breaking point, &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/9ed3e0&#34;&gt;removing the &lt;code&gt;cheat&lt;/code&gt; tool&lt;/a&gt;.
There was nothing wrong with it, but I wasn&amp;rsquo;t using it as often as I thought.
I used this as momentum to move on to my &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commits/main?after=f56b7bf67301f60785f7a7af1ced945d25b4d5c8+174&#34;&gt;other&lt;/a&gt; &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commits/main?before=f56b7bf67301f60785f7a7af1ced945d25b4d5c8+175&#34;&gt;configuration&lt;/a&gt; &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commits/main?before=f56b7bf67301f60785f7a7af1ced945d25b4d5c8+140&#34;&gt;files&lt;/a&gt;, mostly my vimrc.
Previously, I had been fairly lax on my git hygiene for the repo, using commit messages like &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/26852a&#34;&gt;&amp;ldquo;Various updates&amp;rdquo;&lt;/a&gt; or simply a &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/579368&#34;&gt;&amp;quot;✨&amp;quot;&lt;/a&gt;.
I knew that removing all that accumulated knowledge meant that I needed to have better practices, so I started using a &lt;code&gt;tool:&lt;/code&gt; prefix on my commit subjects, and trying to be better about abiding to &lt;a href=&#34;https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention&#34;&gt;atomic commits&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;neovim&#34;&gt;
  (neo)vim &lt;a class=&#34;anchor&#34; href=&#34;#neovim&#34;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;del&gt;Now, my &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commits/main/vim/init.vim&#34;&gt;vimrc&lt;/a&gt; is a lean ~350 lines!&lt;/del&gt;
&lt;ins&gt;Update (August 2022): I&amp;rsquo;m now fully in on the Lua (actually, Fennel) neovim configuration, so the previous link is invalid.&lt;/ins&gt;
(Maybe not lean for a vanilla vim purist, but it&amp;rsquo;s reasonable for me!)
You&amp;rsquo;ll also notice that I&amp;rsquo;ve converted over to neovim fully.
I had a number of settings, plugins, and mappings that were working around some of vim&amp;rsquo;s weirdness, and I was using neovim 98% of the time.
I removed all the settings that were the default in neovim and did a lot of &lt;code&gt;:help&lt;/code&gt; diving to understand the settings that remained.
I also dropped plugins that I used sparingly, some of the filetype specific autocommands, and the sectioning comments used for folding.
I&amp;rsquo;ve added a few plugins back that I wasn&amp;rsquo;t quite able to shake (notably, I tried to learn vim&amp;rsquo;s builtin autocomplete commands, but found &lt;a href=&#34;https://github.com/Shougo/deoplete.nvim&#34;&gt;deoplete&lt;/a&gt; to be something I couldn&amp;rsquo;t avoid using).&lt;/p&gt;
&lt;h2 id=&#34;xdg-base-dir&#34;&gt;
  &lt;a href=&#34;https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html&#34;&gt;XDG Base Dir&lt;/a&gt; &lt;a class=&#34;anchor&#34; href=&#34;#xdg-base-dir&#34;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Switching over to neovim meant that I could remove the hacks around neovim finding vim&amp;rsquo;s configuration directory, and move everything to ~/.config.
I could also leverage neovim&amp;rsquo;s data dir for &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/blob/f56b7bf67301f60785f7a7af1ced945d25b4d5c8/vim/init.vim#L24&#34;&gt;storing my plugins&lt;/a&gt;.
Given the neovim changes, I started to look elsewhere where I could move my dotfiles in ~/.config, and managed to get &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/blob/f56b7bf67301f60785f7a7af1ced945d25b4d5c8/install.conf.yaml#L14-L23&#34;&gt;everything except .tmux.conf&lt;/a&gt; moved over.
This includes my global git configuration, which until now I didn&amp;rsquo;t realize could be anywhere but ~/.gitconfig and ~/.gitignore!&lt;/p&gt;
&lt;h2 id=&#34;other-configuration&#34;&gt;
  other configuration &lt;a class=&#34;anchor&#34; href=&#34;#other-configuration&#34;&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Alongside the effort to move everything to within ~/.config, I ended up removing language specific configuration (mypy, isort, and flake8 config for Python, profiles.clj for Clojure), tooling configuration (curlrc, editorconfig, vale.ini), old package listings (Brewfile), and unused shell configuration (zprofile, zshenv, and zshrc).
Overall, &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/compare/4d1f7f...f56b7b&#34;&gt;I&amp;rsquo;ve reduced my dotfiles down to things that I use &lt;em&gt;now&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And it feels great.&lt;/p&gt;
&lt;p&gt;🛁&lt;/p&gt;
</description>
      <source:markdown><p>Spring is in the air!
Inspired by <a href="https://deletionday.com/">Deletion Day</a>, I&rsquo;ve been tidying up some of my physical and digital life.
I&rsquo;ve deleted old accounts, removed applications that I&rsquo;m not using, and gotten rid of extraneous hardware.
But, the cleanup I&rsquo;m most proud of is my <a href="https://github.com/stefanvanburen/dotfiles">dotfiles</a> cleanse.</p>
<h2 id="the-purge">
  the purge <a class="anchor" href="#the-purge">#</a>
</h2>
<p>For awhile, I was a bit of a dotfiles packrat.
I worried that the knowledge I had gained &mdash; the choices I had made &mdash; would be lost to the depths of time if I were to remove them from the text of the files themselves.
That meant old color schemes, old key bindings, old tips and tricks, all stayed in their respective places.
Perhaps moved around, but not deleted.
Over the years, this meant that my <a href="https://github.com/stefanvanburen/dotfiles/blob/4d1f7f/vimrc">vimrc had bloated up to ~1600 lines</a>!
I had also accumulated a lot of files for tools I&rsquo;m no longer using, that were strewn all over my system.</p>
<p>I finally reached a breaking point, <a href="https://github.com/stefanvanburen/dotfiles/commit/9ed3e0">removing the <code>cheat</code> tool</a>.
There was nothing wrong with it, but I wasn&rsquo;t using it as often as I thought.
I used this as momentum to move on to my <a href="https://github.com/stefanvanburen/dotfiles/commits/main?after=f56b7bf67301f60785f7a7af1ced945d25b4d5c8+174">other</a> <a href="https://github.com/stefanvanburen/dotfiles/commits/main?before=f56b7bf67301f60785f7a7af1ced945d25b4d5c8+175">configuration</a> <a href="https://github.com/stefanvanburen/dotfiles/commits/main?before=f56b7bf67301f60785f7a7af1ced945d25b4d5c8+140">files</a>, mostly my vimrc.
Previously, I had been fairly lax on my git hygiene for the repo, using commit messages like <a href="https://github.com/stefanvanburen/dotfiles/commit/26852a">&ldquo;Various updates&rdquo;</a> or simply a <a href="https://github.com/stefanvanburen/dotfiles/commit/579368">&quot;✨&quot;</a>.
I knew that removing all that accumulated knowledge meant that I needed to have better practices, so I started using a <code>tool:</code> prefix on my commit subjects, and trying to be better about abiding to <a href="https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention">atomic commits</a>.</p>
<h2 id="neovim">
  (neo)vim <a class="anchor" href="#neovim">#</a>
</h2>
<p><del>Now, my <a href="https://github.com/stefanvanburen/dotfiles/commits/main/vim/init.vim">vimrc</a> is a lean ~350 lines!</del>
<ins>Update (August 2022): I&rsquo;m now fully in on the Lua (actually, Fennel) neovim configuration, so the previous link is invalid.</ins>
(Maybe not lean for a vanilla vim purist, but it&rsquo;s reasonable for me!)
You&rsquo;ll also notice that I&rsquo;ve converted over to neovim fully.
I had a number of settings, plugins, and mappings that were working around some of vim&rsquo;s weirdness, and I was using neovim 98% of the time.
I removed all the settings that were the default in neovim and did a lot of <code>:help</code> diving to understand the settings that remained.
I also dropped plugins that I used sparingly, some of the filetype specific autocommands, and the sectioning comments used for folding.
I&rsquo;ve added a few plugins back that I wasn&rsquo;t quite able to shake (notably, I tried to learn vim&rsquo;s builtin autocomplete commands, but found <a href="https://github.com/Shougo/deoplete.nvim">deoplete</a> to be something I couldn&rsquo;t avoid using).</p>
<h2 id="xdg-base-dir">
  <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG Base Dir</a> <a class="anchor" href="#xdg-base-dir">#</a>
</h2>
<p>Switching over to neovim meant that I could remove the hacks around neovim finding vim&rsquo;s configuration directory, and move everything to ~/.config.
I could also leverage neovim&rsquo;s data dir for <a href="https://github.com/stefanvanburen/dotfiles/blob/f56b7bf67301f60785f7a7af1ced945d25b4d5c8/vim/init.vim#L24">storing my plugins</a>.
Given the neovim changes, I started to look elsewhere where I could move my dotfiles in ~/.config, and managed to get <a href="https://github.com/stefanvanburen/dotfiles/blob/f56b7bf67301f60785f7a7af1ced945d25b4d5c8/install.conf.yaml#L14-L23">everything except .tmux.conf</a> moved over.
This includes my global git configuration, which until now I didn&rsquo;t realize could be anywhere but ~/.gitconfig and ~/.gitignore!</p>
<h2 id="other-configuration">
  other configuration <a class="anchor" href="#other-configuration">#</a>
</h2>
<p>Alongside the effort to move everything to within ~/.config, I ended up removing language specific configuration (mypy, isort, and flake8 config for Python, profiles.clj for Clojure), tooling configuration (curlrc, editorconfig, vale.ini), old package listings (Brewfile), and unused shell configuration (zprofile, zshenv, and zshrc).
Overall, <a href="https://github.com/stefanvanburen/dotfiles/compare/4d1f7f...f56b7b">I&rsquo;ve reduced my dotfiles down to things that I use <em>now</em></a>.</p>
<p>And it feels great.</p>
<p>🛁</p>
</source:markdown>
    </item>
    
    <item>
      <title>Book Review: Mr. Penumbra&#39;s 24-hour Bookstore</title>
      <link>https://stefan.vanburen.xyz/blog/mr-penumbras-24-hour-bookstore-review/</link>
      <pubDate>Thu, 26 Dec 2019 20:40:50 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/mr-penumbras-24-hour-bookstore-review/</guid>
      <description>&lt;p&gt;Rating: 4/5 ⭐️&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Warning: Spoilers below&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.worldcat.org/title/mr-penumbras-24-hour-bookstore-a-novel/oclc/1310309732&#34;&gt;Mr. Penumbra&amp;rsquo;s 24-hour bookstore&lt;/a&gt; is an amusing and intriguing tale concerning a new clerk at a strange bookstore who uncovers the mysteries of his new place of work.
Clay (the clerk and main protagonist) discovers a secret society that his employer is a part of, and seeks to bring the mystery to light.&lt;/p&gt;
&lt;p&gt;I came into contact with the title via Robin Sloan&amp;rsquo;s blog for 2019, &lt;a href=&#34;https://desert.glass/&#34;&gt;Year of the Meteor&lt;/a&gt;, which I&amp;rsquo;ve adored.
The assortment of links and life commentary has been wonderful all year, and I&amp;rsquo;m looking forward to Sloan&amp;rsquo;s next project.&lt;/p&gt;
&lt;p&gt;Overall, the book flowed well and moved quickly; I&amp;rsquo;m not a very fast reader and made it through in around 5 and a half hours total, largely over the course of two days.
The writing included a lot of references for a more technical crowd &amp;mdash; or, at least, a crowd familiar with the Silicon Valley startup scene.
There were a handful of mentions of Google specific technologies, as well as allusions to computing history.&lt;/p&gt;
&lt;p&gt;However, I felt the novel was lacking in the department of having a serious antagonist, and a bit too much feeling of Deus ex machina at the end.
The novel&amp;rsquo;s antagonist, Corvina, had essentially no chance of stopping the protagonists from achieving their goal.
Meanwhile, when everything seemed lost after a failed attempt at code-breaking with the entirety of Google&amp;rsquo;s compute power, and the rest of the novel being largely about the protagonist&amp;rsquo;s ability to bring together his eclectic set of friends&amp;rsquo; talents to bear on the mystery at hand, the story is resolved by Clay on his own.&lt;/p&gt;
&lt;p&gt;A feel-good ending was somewhat ruined by the quickness of the last third of the book.
But still, I was entertained the entire way, and recommend giving it a read.&lt;/p&gt;
</description>
      <source:markdown><p>Rating: 4/5 ⭐️</p>
<p><strong><em>Warning: Spoilers below</em></strong></p>
<p><a href="https://www.worldcat.org/title/mr-penumbras-24-hour-bookstore-a-novel/oclc/1310309732">Mr. Penumbra&rsquo;s 24-hour bookstore</a> is an amusing and intriguing tale concerning a new clerk at a strange bookstore who uncovers the mysteries of his new place of work.
Clay (the clerk and main protagonist) discovers a secret society that his employer is a part of, and seeks to bring the mystery to light.</p>
<p>I came into contact with the title via Robin Sloan&rsquo;s blog for 2019, <a href="https://desert.glass/">Year of the Meteor</a>, which I&rsquo;ve adored.
The assortment of links and life commentary has been wonderful all year, and I&rsquo;m looking forward to Sloan&rsquo;s next project.</p>
<p>Overall, the book flowed well and moved quickly; I&rsquo;m not a very fast reader and made it through in around 5 and a half hours total, largely over the course of two days.
The writing included a lot of references for a more technical crowd &mdash; or, at least, a crowd familiar with the Silicon Valley startup scene.
There were a handful of mentions of Google specific technologies, as well as allusions to computing history.</p>
<p>However, I felt the novel was lacking in the department of having a serious antagonist, and a bit too much feeling of Deus ex machina at the end.
The novel&rsquo;s antagonist, Corvina, had essentially no chance of stopping the protagonists from achieving their goal.
Meanwhile, when everything seemed lost after a failed attempt at code-breaking with the entirety of Google&rsquo;s compute power, and the rest of the novel being largely about the protagonist&rsquo;s ability to bring together his eclectic set of friends&rsquo; talents to bear on the mystery at hand, the story is resolved by Clay on his own.</p>
<p>A feel-good ending was somewhat ruined by the quickness of the last third of the book.
But still, I was entertained the entire way, and recommend giving it a read.</p>
</source:markdown>
    </item>
    
    <item>
      <title>`extract` in fish</title>
      <link>https://stefan.vanburen.xyz/blog/fish-extract/</link>
      <pubDate>Thu, 14 Nov 2019 10:40:55 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/fish-extract/</guid>
      <description>&lt;p&gt;I recently switched from &lt;a href=&#34;https://en.wikipedia.org/wiki/Z_shell&#34;&gt;zsh&lt;/a&gt; to &lt;a href=&#34;https://fishshell.com/&#34;&gt;fish&lt;/a&gt; as my shell of choice.
I liked the idea of starting from scratch, with the sane defaults that fish provides, as my &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/commit/10d9acc84179425772597d5a4c34c70a8bddd906#diff-53cae0c7df819f6e6a8104beae0f53a1&#34;&gt;zsh configuration files were getting a bit out of control&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the most part, the transition was fairly painless and straightforward.
However, I still miss a few of the zsh niceties that I had been used to over the years &amp;mdash; one being &lt;a href=&#34;https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/extract/extract.plugin.zsh&#34;&gt;&lt;code&gt;extract&lt;/code&gt; function provided in oh-my-zsh&lt;/a&gt;.
Even after moving away from oh-my-zsh as my plugin manager in zsh, I had used my new manager to grab &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/blob/74dd7a02b83ca1874d721e242e0f466ca1f65692/zshrc#L13-L14&#34;&gt;just that plugin&lt;/a&gt; for my usage.&lt;/p&gt;
&lt;p&gt;After a cursory google, it seemed like a fish port of the plugin didn&amp;rsquo;t exist, so I decided to try to port over the plugin myself.
You can find the whole function &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/blob/9e62163c674f3fef58a12d752daa78b4c5eeecbe/config.fish#L65-L125&#34;&gt;in my config.fish in my dotfiles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, we&amp;rsquo;ll define a function named &lt;code&gt;extract&lt;/code&gt; and give it a description.
I&amp;rsquo;ve also noted in a comment where this function was ported from.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-d&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;extract files from archives&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;# largely adapted from
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;# https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/extract
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The function first checks if we have arguments &amp;mdash; if we have none, there&amp;rsquo;s nothing to do, so we&amp;rsquo;ll echo a usage string to stderr and exit.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c&#34;&gt;# no arguments, write usage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;count&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$argv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-eq&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Usage: extract [-option] [file ...] &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; Options:&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34; -r, --remove    Remove archive after unpacking.&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we check to see if the &lt;code&gt;-r&lt;/code&gt; / &lt;code&gt;--remove&lt;/code&gt; option has been supplied as the first argument.
If it is, we&amp;rsquo;ll remove the archive files after they&amp;rsquo;ve been successfully unarchived.
We also remove this argument so that our main loop can iterate correctly over the filenames.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;remove_file&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$argv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-r&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$argv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;--remove&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;remove_file&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;--erase&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now comes the main loop, which iterates over the filenames supplied.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$argv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..-&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, we ensure that the filename arguments are valid files:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;test&lt;/span&gt; ! &lt;span class=&#34;na&#34;&gt;-f&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;extract: &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39; is not a valid file&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We default our success value to &lt;code&gt;0&lt;/code&gt; &amp;mdash; if the file&amp;rsquo;s extension isn&amp;rsquo;t something we can deal with, we&amp;rsquo;ll set this to &lt;code&gt;1&lt;/code&gt; so that we can avoid removing the file even if the remove option is set.
Then, we grab the extension via some fish regex matching, using the &lt;code&gt;string match&lt;/code&gt; function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;success&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;extension&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;string &lt;/span&gt;match &lt;span class=&#34;na&#34;&gt;-r&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.*(\.[^\.]*)\$&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, we&amp;rsquo;ve reached the main switch statement, which is largely a translation of the &lt;code&gt;zsh&lt;/code&gt; version&amp;rsquo;s unarchiving calls to &lt;code&gt;fish&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$extension&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tar.gz&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tgz&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; xv&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; zxvf &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tar.bz2&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tbz&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tbz2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; xvjf &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tar.xz&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.txz&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;--xz&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-xvf&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;xzcat&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; xvf &lt;span class=&#34;na&#34;&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tar.zma&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tlz&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;--lzma&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-xvf&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;lzcat&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; xvf &lt;span class=&#34;na&#34;&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.tar&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;tar&lt;/span&gt; xvf &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.gz&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;gunzip&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-k&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.bz2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;bunzip2&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.xz&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;unxz&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.lzma&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;unlzma&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.z&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;uncompress&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.zip&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.war&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.jar&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.sublime-package&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.ipsw&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.xpi&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.apk&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.aar&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.whl&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;extract_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;string &lt;/span&gt;match &lt;span class=&#34;na&#34;&gt;-r&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;(.*)\.[^\.]*\$&amp;#34;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)[&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;unzip&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-d&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$extract_dir&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.rar&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;unrar&lt;/span&gt; x &lt;span class=&#34;na&#34;&gt;-ad&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*.7z&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;7za&lt;/span&gt; x &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;extract: &amp;#39;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#39; cannot be extracted&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;success&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we&amp;rsquo;ll remove the original file if we&amp;rsquo;ve successfully unarchived the file, and end the loop and the function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fish&#34; data-lang=&#34;fish&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$success&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-eq&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$remove_file&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;-eq&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;rm&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was my first experience trying to port a larger function from zsh to fish, and it definitely took some playing around with the various test functions to get it right.
Also, the &lt;code&gt;string match&lt;/code&gt; functions were largely cobbled together from StackOverflow.
I strongly suggest &lt;a href=&#34;https://github.com/stefanvanburen/dotfiles/blob/9e62163c674f3fef58a12d752daa78b4c5eeecbe/config.fish#L21&#34;&gt;aliasing this function to &lt;code&gt;x&lt;/code&gt;&lt;/a&gt;, or some other short sequence, for easier usage.&lt;/p&gt;
&lt;p&gt;And voilà, we have a working general purpose extraction function, in fish!&lt;/p&gt;
&lt;p&gt;🐠&lt;/p&gt;
</description>
      <source:markdown><p>I recently switched from <a href="https://en.wikipedia.org/wiki/Z_shell">zsh</a> to <a href="https://fishshell.com/">fish</a> as my shell of choice.
I liked the idea of starting from scratch, with the sane defaults that fish provides, as my <a href="https://github.com/stefanvanburen/dotfiles/commit/10d9acc84179425772597d5a4c34c70a8bddd906#diff-53cae0c7df819f6e6a8104beae0f53a1">zsh configuration files were getting a bit out of control</a>.</p>
<p>For the most part, the transition was fairly painless and straightforward.
However, I still miss a few of the zsh niceties that I had been used to over the years &mdash; one being <a href="https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/extract/extract.plugin.zsh"><code>extract</code> function provided in oh-my-zsh</a>.
Even after moving away from oh-my-zsh as my plugin manager in zsh, I had used my new manager to grab <a href="https://github.com/stefanvanburen/dotfiles/blob/74dd7a02b83ca1874d721e242e0f466ca1f65692/zshrc#L13-L14">just that plugin</a> for my usage.</p>
<p>After a cursory google, it seemed like a fish port of the plugin didn&rsquo;t exist, so I decided to try to port over the plugin myself.
You can find the whole function <a href="https://github.com/stefanvanburen/dotfiles/blob/9e62163c674f3fef58a12d752daa78b4c5eeecbe/config.fish#L65-L125">in my config.fish in my dotfiles</a>.</p>
<p>First, we&rsquo;ll define a function named <code>extract</code> and give it a description.
I&rsquo;ve also noted in a comment where this function was ported from.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="k">function</span> <span class="nf">extract</span> <span class="na">-d</span> <span class="s2">&#34;extract files from archives&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c"># largely adapted from
</span></span></span><span class="line"><span class="cl">    <span class="c"># https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/extract
</span></span></span></code></pre></div><p>The function first checks if we have arguments &mdash; if we have none, there&rsquo;s nothing to do, so we&rsquo;ll echo a usage string to stderr and exit.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">    <span class="c"># no arguments, write usage
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="k">test</span> <span class="o">(</span><span class="nf">count</span> <span class="nv">$argv</span><span class="o">)</span> <span class="na">-eq</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s2">&#34;Usage: extract [-option] [file ...] &#34;</span> <span class="o">&gt;&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s2">&#34; Options:&#34;</span> <span class="o">&gt;&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="k">echo</span> <span class="s2">&#34; -r, --remove    Remove archive after unpacking.&#34;</span> <span class="o">&gt;&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">        <span class="k">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span></code></pre></div><p>Next, we check to see if the <code>-r</code> / <code>--remove</code> option has been supplied as the first argument.
If it is, we&rsquo;ll remove the archive files after they&rsquo;ve been successfully unarchived.
We also remove this argument so that our main loop can iterate correctly over the filenames.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">    <span class="k">set</span> <span class="nv">remove_file</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="k">test</span> <span class="nv">$argv</span><span class="o">[</span><span class="m">1</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;-r&#34;</span><span class="p">;</span> <span class="k">or</span> <span class="k">test</span> <span class="nv">$argv</span><span class="o">[</span><span class="m">1</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;--remove&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">remove_file</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="na">--erase</span> <span class="nv">argv</span><span class="o">[</span><span class="m">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span></code></pre></div><p>Now comes the main loop, which iterates over the filenames supplied.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">    <span class="k">for</span> <span class="nv">i</span> <span class="k">in</span> <span class="nv">$argv</span><span class="o">[</span><span class="m">1</span><span class="o">..-</span><span class="m">1</span><span class="o">]</span>
</span></span></code></pre></div><p>First, we ensure that the filename arguments are valid files:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">        <span class="k">if</span> <span class="k">test</span> ! <span class="na">-f</span> <span class="nv">$i</span>
</span></span><span class="line"><span class="cl">            <span class="k">echo</span> <span class="s2">&#34;extract: &#39;</span><span class="nv">$i</span><span class="s2">&#39; is not a valid file&#34;</span> <span class="o">&gt;&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span></code></pre></div><p>We default our success value to <code>0</code> &mdash; if the file&rsquo;s extension isn&rsquo;t something we can deal with, we&rsquo;ll set this to <code>1</code> so that we can avoid removing the file even if the remove option is set.
Then, we grab the extension via some fish regex matching, using the <code>string match</code> function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">success</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">extension</span> <span class="o">(</span><span class="nb">string </span>match <span class="na">-r</span> <span class="s2">&#34;.*(\.[^\.]*)\$&#34;</span> <span class="nv">$i</span><span class="o">)[</span><span class="m">2</span><span class="o">]</span>
</span></span></code></pre></div><p>Now, we&rsquo;ve reached the main switch statement, which is largely a translation of the <code>zsh</code> version&rsquo;s unarchiving calls to <code>fish</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">        <span class="k">switch</span> <span class="nv">$extension</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.tar.gz&#39;</span> <span class="s1">&#39;*.tgz&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">tar</span> xv<span class="p">;</span> <span class="k">or</span> <span class="nf">tar</span> zxvf <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.tar.bz2&#39;</span> <span class="s1">&#39;*.tbz&#39;</span> <span class="s1">&#39;*.tbz2&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">tar</span> xvjf <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.tar.xz&#39;</span> <span class="s1">&#39;*.txz&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">tar</span> <span class="na">--xz</span> <span class="na">-xvf</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">or</span> <span class="nf">xzcat</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span> <span class="o">|</span> <span class="nf">tar</span> xvf <span class="na">-
</span></span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.tar.zma&#39;</span> <span class="s1">&#39;*.tlz&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">tar</span> <span class="na">--lzma</span> <span class="na">-xvf</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">or</span> <span class="nf">lzcat</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span> <span class="o">|</span> <span class="nf">tar</span> xvf <span class="na">-
</span></span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.tar&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">tar</span> xvf <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.gz&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">gunzip</span> <span class="na">-k</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.bz2&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">bunzip2</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.xz&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">unxz</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.lzma&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">unlzma</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.z&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">uncompress</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.zip&#39;</span> <span class="s1">&#39;*.war&#39;</span> <span class="s1">&#39;*.jar&#39;</span> <span class="s1">&#39;*.sublime-package&#39;</span> <span class="s1">&#39;*.ipsw&#39;</span> <span class="s1">&#39;*.xpi&#39;</span> <span class="s1">&#39;*.apk&#39;</span> <span class="s1">&#39;*.aar&#39;</span> <span class="s1">&#39;*.whl&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="k">set</span> <span class="nv">extract_dir</span> <span class="o">(</span><span class="nb">string </span>match <span class="na">-r</span> <span class="s2">&#34;(.*)\.[^\.]*\$&#34;</span> <span class="nv">$i</span><span class="o">)[</span><span class="m">2</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">                <span class="nf">unzip</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span> <span class="na">-d</span> <span class="nv">$extract_dir</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.rar&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">unrar</span> x <span class="na">-ad</span> <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*.7z&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="nf">7za</span> x <span class="s2">&#34;</span><span class="nv">$i</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s1">&#39;*&#39;</span>
</span></span><span class="line"><span class="cl">                <span class="k">echo</span> <span class="s2">&#34;extract: &#39;</span><span class="nv">$i</span><span class="s2">&#39; cannot be extracted&#34;</span> <span class="o">&gt;&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">                <span class="k">set</span> <span class="nv">success</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span></code></pre></div><p>Finally, we&rsquo;ll remove the original file if we&rsquo;ve successfully unarchived the file, and end the loop and the function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">        <span class="k">if</span> <span class="k">test</span> <span class="nv">$success</span> <span class="na">-eq</span> <span class="m">0</span><span class="p">;</span> <span class="k">and</span> <span class="k">test</span> <span class="nv">$remove_file</span> <span class="na">-eq</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">            <span class="nf">rm</span> <span class="nv">$i</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><p>This was my first experience trying to port a larger function from zsh to fish, and it definitely took some playing around with the various test functions to get it right.
Also, the <code>string match</code> functions were largely cobbled together from StackOverflow.
I strongly suggest <a href="https://github.com/stefanvanburen/dotfiles/blob/9e62163c674f3fef58a12d752daa78b4c5eeecbe/config.fish#L21">aliasing this function to <code>x</code></a>, or some other short sequence, for easier usage.</p>
<p>And voilà, we have a working general purpose extraction function, in fish!</p>
<p>🐠</p>
</source:markdown>
    </item>
    
    <item>
      <title>Hello, world!</title>
      <link>https://stefan.vanburen.xyz/blog/hello-world/</link>
      <pubDate>Wed, 13 Nov 2019 14:47:53 -0500</pubDate>
      <guid>https://stefan.vanburen.xyz/blog/hello-world/</guid>
      <description>&lt;p&gt;Just an introductory post here, nothing to see.&lt;/p&gt;
</description>
      <source:markdown><p>Just an introductory post here, nothing to see.</p>
</source:markdown>
    </item>
    
  </channel>
</rss>
