<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Chris DeLuca</title>
    <description>The latest articles on Forem by Chris DeLuca (@bronzehedwick).</description>
    <link>https://forem.com/bronzehedwick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F92508%2F8462d6be-2870-4b8e-8a93-64d4c48ae23e.jpeg</url>
      <title>Forem: Chris DeLuca</title>
      <link>https://forem.com/bronzehedwick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bronzehedwick"/>
    <language>en</language>
    <item>
      <title>Use Neovim as your man pager</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Tue, 08 Mar 2022 00:30:46 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/use-neovim-as-your-man-pager-3ilf</link>
      <guid>https://forem.com/bronzehedwick/use-neovim-as-your-man-pager-3ilf</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/use-neovim-as-your-man-pager/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You may know that you can open man pages in a Neovim buffer with &lt;code&gt;:Man&lt;/code&gt;. However, you can also configure your shell to open manual pages in a Neovim buffer when called from the command line.&lt;/p&gt;

&lt;p&gt;First, if you’re unfamiliar, Neovim ships with the great &lt;a href="https://neovim.io/doc/user/filetype.html#:Man"&gt;&lt;code&gt;:Man&lt;/code&gt;&lt;/a&gt;command, which opens man pages in a nicely formatted buffer. These buffers are normal Vim buffers, so come equipped with syntax highlighting, can be easily searched, and links to other manual pages can be followed with C-].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" Open the git manual page.&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Man git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also open man pages invoked inside Neovim’s terminal emulator using this same man buffer with a little configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This opens a man buffer?&lt;/span&gt;
man git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;man&lt;/code&gt; command can be configured to render pages with any program, controlled by the &lt;code&gt;$MANPAGER&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;We could set &lt;code&gt;$MANPAGER&lt;/code&gt; to &lt;code&gt;nvim&lt;/code&gt;, but that would cause nesting Neovim instances if called from inside a Neovim &lt;code&gt;:terminal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To work around this, we’ll need help from the &lt;a href="https://github.com/mhinz/neovim-remote"&gt;neovim-remote&lt;/a&gt;project (at least until Neovim core &lt;a href="https://github.com/neovim/neovim/issues/1750"&gt;adds &lt;code&gt;--remote&lt;/code&gt;back&lt;/a&gt;). With that installed, we can call &lt;code&gt;nvr&lt;/code&gt; inside a Neovim terminal buffer to open the given file in the same Neovim instance.&lt;/p&gt;

&lt;p&gt;I personally would rather not launch a whole Neovim instance just to render a man page if I’m not already inside Neovim, so for this tip we’ll add some detection code to only set the &lt;code&gt;$MANPAGER&lt;/code&gt;value inside Neovim. We can do this by checking the value of the&lt;code&gt;$NVIM_LISTEN_ADDRESS&lt;/code&gt; environment variable, which will only be set inside an instance of Neovim.&lt;/p&gt;

&lt;p&gt;We’ll use the &lt;code&gt;-o&lt;/code&gt; flag to open the man page in a new split, to help retain the context of what you’re working on.&lt;/p&gt;

&lt;p&gt;In your bash/zsh config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NVIM_LISTEN_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;+x&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
 &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MANPAGER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bin/nvr -c 'Man!' -o -"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for the fish shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if test -n "$NVIM_LISTEN_ADDRESS"
 set -x MANPAGER "/usr/local/bin/nvr -c 'Man!' -o -"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it. Happy RTFM!&lt;/p&gt;

</description>
      <category>neovim</category>
    </item>
    <item>
      <title>Base CSS to Style a Novel</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Thu, 27 Jan 2022 00:09:39 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/base-css-to-style-a-novel-3fbk</link>
      <guid>https://forem.com/bronzehedwick/base-css-to-style-a-novel-3fbk</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/base-css-to-style-a-novel/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’m working on a short novel, which got me thinking about typesetting (naturally). There’s as many ways to style a book as there are a webpage [citation needed], but there’s rules and conventions just the same. I’m a web developer by trade, so I decided to try my hand at styling a novel in CSS.&lt;/p&gt;

&lt;p&gt;Here, then, is my attempt at a minimal, base stylesheet that captures many standard print conventions. The goal here is to have something that looks good out of the box, is a solid base to build on top of if need be, and most importantly, looks like a novel.&lt;/p&gt;

&lt;p&gt;Something pleasant I discovered is that browser defaults are largely what you want for a novel, which is unsurprising when you think about the history of the web platform starting in academia.&lt;/p&gt;

&lt;p&gt;I’ve annotated my code with explanations for each rule, below; you can &lt;a href="https://www.chrisdeluca.me/article/base-css-to-style-a-novel/documents/novel.css"&gt;download a copy of the code here&lt;/a&gt;, and you can see the &lt;a href="https://codepen.io/bronzehedwick/pen/xxPbPwX"&gt;results in Codepen&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/*! https://chrisdeluca.me/article/base-css-to-style-a-novel
    License: MIT
 */&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Starting with the hard stuff: fonts. I chose Palatino because it's a
     * classic serif font that's loaded on many systems by default. This would
     * be the first thing to change to customize the look and feel. The rest of
     * the rules are written using font-relative units, so changing the font
     * family here wont change the other sizing ratios.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Palatino&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * The standard base font size for print is 12pt, which equals 16px, the
     * browser default. I increased the value by 2 points since Palatino runs
     * small, so this should probably go back to 12pt if the font family is
     * different.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14pt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Increase the line height to 1.6 times the font size. The browser default is
     * much smaller, about 1.2 times the font size on desktop browsers, which
     * feels cramped—more like a textbook than a novel. The value 1.6 is
     * somewhat arbitrary, so play around with it, but do make sure the value
     * is unitless. See:
     * https://css-tricks.com/almanac/properties/l/line-height/#aa-unitless-line-heights
     */&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Novels are the definition of a long read, so I' doing everyone's eyes a
     * solid by optimizing for legibility, aka glyph clarity, rather than
     * render speed or correctness, which are either secondary or irrelevant,
     * depending on the medium.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;text-rendering&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;optimizeLegibility&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Remove any top margin and center the content. Our headings will take
     * care of any needed vertical spacing, and making sure content is centered
     * is a nice win for readability on large browser windows.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * The ideal line length for readability is, depending on the study you're
     * citing and the font, between 50–75 characters. I'm more or less
     * splitting the difference. I use the lesser known ch unit—equal to the
     * width of the font's "0" character—for horizontal spacing like this, as
     * it feels natural and is easy to reference without doing extra math.
     * Since the zero character is usually one of, if not the, widest character
     * in a font, the exact number of characters per line will be slightly more
     * than the given value, which is perfectly acceptable. I'm also setting
     * this as a max-width instead of a width, since we want our content to
     * wrap nicely if the screen size is smaller than the desired value, rather
     * than being cut off.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;65ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * Use the semantic &amp;lt;header&amp;gt; tag to mark up the title page. This text is always
 * centered, and, if we're outputting to printed media, we want this to be on
 * it's own page.
 */&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;break-after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * Headings, aka for chapters or sections, should be spaced apart from all
 * other content, but should be closer to the content they define. For example,
 * the heading "Chapter 2" should be closer to the content of chapter 2 than to
 * chapter 1's content, to show its association. Setting these values in ems
 * lets us define all the headings in one sweepingly consistent declaration.
 */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;h6&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * A new chapter should always start on a new page in printed media. Since the
 * title of the book would be an &amp;lt;h1&amp;gt;, I'm assuming only &amp;lt;h2&amp;gt;s are chapter
 * titles. Any heading below an &amp;lt;h2&amp;gt; would be a section title of some sort, and
 * not warrant a new page (and probably wouldn't appear in a novel, anyway,
 * being relegated to more formally structured content like textbooks).
 */&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;break-before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * Browsers default to "web style" paragraph delineation, aka whitespace
 * between each one. Most books are not set this way, instead using text
 * indentation (see below). Get rid of that space between.
 */&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * "Book style" paragraph delineation uses indentation for each paragraph after
 * the first. Presumably, you don't need the indentation for the first
 * paragraph, since there's a chapter heading or whitespace or just not text
 * above it to indicate that what you're reading is, indeed, a paragraph. The
 * same is true for any other non-paragraph element, aka a list or an image.
 * Using the adjacent sibling combinator, we match every paragraph that follows
 * another paragraph, and add our text indent. This is conceptually different
 * but has the same effect here as giving every paragraph a text indent, and
 * then removing it for :first-of-type. For the indent itself, a value of about
 * two characters is pretty common in print, but occasionally I've seen four is
 * used as well.
 */&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-indent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * The horizontal rule element, now appropriately re-appropriated as the
 * thematic break element. Thematic breaks take many forms in different
 * printings—a plain horizontal line, a decorative, squiggly line, a glyph
 * that looks like a cross between a heart and a radish (this thing: ❧)—but
 * the most simple display, and one that will often be present even within
 * books that feature a fancy break as well, is plain old whitespace. We could
 * achieve this with empty paragraph tags, or a couple of &amp;lt;br&amp;gt; tags, but that's
 * not semantic, is hard to type in markdown, and makes me want to vomit.
 * We can do better.
 */&lt;/span&gt;
&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * What if we want a "harder" thematic break than just plain whitespace? As
 * mentioned above, you'll see this in novels a fair amount, mixing both
 * whitespace and a visual thematic break. Again, airing on the side
 * of simplicity and commonness, I' using the "three stars" break, which is
 * three star symbols centered at a slightly larger font size. This could have
 * been implemented with a class, but I opted for a data attribute so I could
 * only style &amp;lt;hr&amp;gt; elements (enforcing semantics) while not increasing the
 * specificity too much.
 */&lt;/span&gt;
&lt;span class="nt"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-break&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"hard"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Increase the font size a bit, using ems to keep ratios consistent. A
     * visible thematic break should be, well, visible, and having it at the
     * same size as the text diminishes it somehow.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Add spacing above and below. I set with ems, as this inherits the larger
     * font size we set above, giving this a little bit more room.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Browser defaults for the &amp;lt;hr&amp;gt; element are a grey color, receding the
     * content into the background, but we want our break to stand out (it's a
     * break after all), and we want it to work in print. Although this will
     * probably always be black, I use the currentColor keyword to pick up
     * whatever the text color is as future proofing (dark mode, anyone?)
     */&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/**
     * Center that thematic break. The stars are added using pseudo content,
     * which default to inline display, so we don't need anything fancier than
     * text-align here.
     */&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/**
 * Actually add the stars in, using pseudo content.
 */&lt;/span&gt;
&lt;span class="nt"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-break&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"hard"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"* * *"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>css</category>
    </item>
    <item>
      <title>DIY Neovim fzy search</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Wed, 12 Jan 2022 21:34:06 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/diy-neovim-fzy-search-25lo</link>
      <guid>https://forem.com/bronzehedwick/diy-neovim-fzy-search-25lo</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/diy-neovim-fzy-search/" rel="noopener noreferrer"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are plenty of fuzzy search solutions for Neovim, most notably &lt;a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer"&gt;Telescope&lt;/a&gt;, but sometimes you just want something fast and simple.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://github.com/jhawthorn/fzy" rel="noopener noreferrer"&gt;&lt;code&gt;fzy&lt;/code&gt;&lt;/a&gt;, a fast command line program with a slick search algorithm. It is a good unix citizen, operating on newline delimited lists passed through&lt;a href="http://www.linfo.org/standard_input.html" rel="noopener noreferrer"&gt;&lt;code&gt;stdin&lt;/code&gt;&lt;/a&gt;, making it easy to integrate into all sorts of tools, including editors.&lt;/p&gt;

&lt;h3&gt;Helpful Reading&lt;/h3&gt;

&lt;p&gt;During this process, I read a lot on Lua in general and Neovim specifically.&lt;/p&gt;

&lt;p&gt;The four most helpful resources, I found, were the &lt;a href="https://github.com/nanotee/nvim-lua-guide" rel="noopener noreferrer"&gt;Neovim Lua
Guide&lt;/a&gt;
, by Timothée Sterle, Heiker
Curiel’s post on &lt;a href="https://vonheikemen.github.io/devlog/tools/configuring-neovim-using-lua/" rel="noopener noreferrer"&gt;configuring Neovim with
Lua&lt;/a&gt;,
the &lt;a href="https://learnxinyminutes.com/docs/lua/" rel="noopener noreferrer"&gt;Learn Lua in 15 minutes &lt;/a&gt;
guide on “Learn X in Y minutes”,
and the official &lt;a href="https://www.lua.org/pil/" rel="noopener noreferrer"&gt;Programming in Lua &lt;/a&gt;
book, by
Roberto Ierusalimschy.&lt;/p&gt;


&lt;p&gt;It’s own documentation shows an example integration with Vim. However, that implementation relies on the &lt;a href="https://vimhelp.org/eval.txt.html#system%28%29" rel="noopener noreferrer"&gt;&lt;code&gt;system()&lt;/code&gt; &lt;/a&gt;function to display the fuzzy finder, which &lt;a href="https://neovim.io/doc/user/eval.html#system%28%29" rel="noopener noreferrer"&gt;no longer works for interactive commands in Neovim &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yes, there is a &lt;a href="https://github.com/romgrk/fzy-lua-native" rel="noopener noreferrer"&gt;fzy plugin for neovim &lt;/a&gt;, but why not take the opportunity to learn some Neovim Lua, and write an implementation ourselves.&lt;/p&gt;

&lt;p&gt;Along the way, we’ll learn how to load and test Lua files, invoke floating windows, handle interactive terminal inputs, create flexible functions, and add mappings.&lt;/p&gt;

&lt;p&gt;This guide assumes some familiarity with Vim/Neovim, as well as a basic understanding of Lua. If you’re unfamiliar with Lua, I’d recommend reading &lt;a href="https://learnxinyminutes.com/docs/lua/" rel="noopener noreferrer"&gt;Learn Lua in 15 minutes &lt;/a&gt;before starting. If that sounds fun, fire up your terminal and follow along. Otherwise, skip to the end for the final script.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup ¶
&lt;/h2&gt;

&lt;p&gt;Neovim picks up Lua files to include in the &lt;code&gt;lua&lt;/code&gt; folder, so we’ll create a file there called &lt;code&gt;fuzzy-search.lua&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;XDG_CONFIG_HOME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.config&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/nvim/lua"&lt;/span&gt;
nvim &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;XDG_CONFIG_HOME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.config&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/nvim/lua/fuzzy-search.lua"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll need a function for our fuzzy searching, so let’s add one with a debug value to test. We need to access this function from anywhere, so we’ll make it global by omitting the &lt;code&gt;local&lt;/code&gt; keyword. By convention, global variables in Lua start with an uppercase letter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello, search!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neovim provides some handy methods for loading Lua files and functions. We’ll use &lt;a href="https://neovim.io/doc/user/lua.html#:luafile" rel="noopener noreferrer"&gt;&lt;code&gt;luafile&lt;/code&gt; &lt;/a&gt;to load our &lt;code&gt;fuzzy-search.lua&lt;/code&gt; into Neovim’s memory, and the &lt;a href="https://neovim.io/doc/user/lua.html#:lua" rel="noopener noreferrer"&gt;&lt;code&gt;lua&lt;/code&gt; &lt;/a&gt;command to then call our newly added &lt;code&gt;FuzzySearch&lt;/code&gt; command while we’re testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;luafile&lt;/span&gt; % " Interpret the current &lt;span class="k"&gt;file&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;lua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;lua&lt;/span&gt; FuzzySearch&lt;span class="p"&gt;()&lt;/span&gt; " Should &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;'Hello, search!'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; the message area&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll need to re-run those two commands every time we make a change to see their effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summoning the floating window ¶
&lt;/h2&gt;

&lt;p&gt;We can no longer use the &lt;code&gt;system()&lt;/code&gt; hack to interact with terminal programs inside Neovim, but we have access to something better: floating windows! We &lt;em&gt;could&lt;/em&gt; make it a split buffer, but since a search interface is an ephemeral UI component that is fine to overlap existing content and should be dismissed the moment a selection is made, a floating window seems ideal.&lt;/p&gt;

&lt;p&gt;To do this, Neovim provides the &lt;a href="https://neovim.io/doc/user/api.html#nvim_open_win%28%29" rel="noopener noreferrer"&gt;&lt;code&gt;nvim_open_win()&lt;/code&gt; &lt;/a&gt;API method, which we can access from the &lt;code&gt;vim.api&lt;/code&gt; Lua table. This method takes 3 arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;{buffer}&lt;/code&gt;, for which buffer to display, by buffer ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{enter}&lt;/code&gt;, boolean for whether to enter the window or not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{config}&lt;/code&gt;, a table of options.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For &lt;code&gt;{buffer}&lt;/code&gt;, we ultimately want to display a new terminal buffer with the search, so we’ll need to create one here. We’ll use the&lt;a href="https://neovim.io/doc/user/api.html#nvim_create_buf%28%29" rel="noopener noreferrer"&gt;&lt;code&gt;nvim_create_buf&lt;/code&gt; &lt;/a&gt;API method to create a fresh buffer, and we’ll start a terminal session inside it in a later step. &lt;code&gt;nvim_create_buf&lt;/code&gt;returns the ID of the buffer it just created, so it can be passed to&lt;code&gt;nvim_open_win()&lt;/code&gt; directly. It has 2 boolean arguments; the first for whether the buffer will be “listed” by commands like &lt;a href="https://neovim.io/doc/user/windows.html#:buffers" rel="noopener noreferrer"&gt;&lt;code&gt;:ls&lt;/code&gt; &lt;/a&gt;, and the second for if it should be treated as a “scratch” buffer, which sets some options common to throw-away work. Since this is a temporary window, we’ll want to set this to unlisted and scratch.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;{enter}&lt;/code&gt;, we want to start typing our search as soon as the popup window is invoked, without having to do C-w C-l or whatever, so we’ll set this to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So far, our function should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, for &lt;code&gt;{config}&lt;/code&gt;, we’ll be setting several options here, largely to position the window. There are five required properties,&lt;code&gt;relative/external&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;col&lt;/code&gt;, and &lt;code&gt;row&lt;/code&gt;, so let’s set them first.&lt;/p&gt;

&lt;p&gt;Every Neovim window requires either the &lt;code&gt;relative&lt;/code&gt; or &lt;code&gt;external&lt;/code&gt; key to be set. &lt;code&gt;external&lt;/code&gt; is only relevant for external GUI applications, so we’ll keep it simple and only set &lt;code&gt;relative&lt;/code&gt;. &lt;code&gt;relative&lt;/code&gt; controls where the window is positioned relative to, aka, where it’s x/y position originates from. Our window can be relative to the editor, the current window, or the cursor position. This is a global search, so we’ll set&lt;code&gt;relative&lt;/code&gt; to &lt;code&gt;editor&lt;/code&gt;. This means that our new window’s 0/0 x and y position starts at the 0/0 x and y values of the entire editor.&lt;/p&gt;

&lt;p&gt;Width and height are simple: how many rows, for height, and columns, for width, does our window occupy? Let’s keep this straight forward for now, and set &lt;code&gt;width&lt;/code&gt; to 10 and &lt;code&gt;height&lt;/code&gt; to 5.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;col&lt;/code&gt; and &lt;code&gt;row&lt;/code&gt; control where on the grid the window should appear from. This is our starting x and y values. Again, let’s keep this simple and set each to 0.&lt;/p&gt;

&lt;p&gt;Our function should now look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you run &lt;code&gt;luafile&lt;/code&gt; on your &lt;code&gt;fuzzy-search.lua&lt;/code&gt; file again, and then &lt;code&gt;lua FuzzySearch()&lt;/code&gt;, our floating window should appear over the top right of your editor!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fbasic-floating-window.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fbasic-floating-window.png" alt="Our basic floating window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Type &lt;code&gt;:bd&lt;/code&gt; to close it.&lt;/p&gt;

&lt;p&gt;Great, we have a floating window, but it’s not going to be very helpful looking like a postage stamp in the upper left. Let’s adjust the size, and center the window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Centering the window ¶
&lt;/h3&gt;

&lt;p&gt;To center the window, we’ll need to calculate the mid-point for our window’s horizontal and vertical edge based on the window size and the size of Neovim itself, with our good friend Math.&lt;/p&gt;

&lt;p&gt;We can get the width of the editor via the &lt;a href="https://neovim.io/doc/user/options.html#%27columns%27" rel="noopener noreferrer"&gt;&lt;code&gt;columns&lt;/code&gt; &lt;/a&gt;global option, exposed in the &lt;a href="https://neovim.io/doc/user/lua.html#vim.o" rel="noopener noreferrer"&gt;&lt;code&gt;vim.o&lt;/code&gt; &lt;/a&gt;options table, and the height via&lt;a href="https://neovim.io/doc/user/options.html#%27lines%27" rel="noopener noreferrer"&gt;&lt;code&gt;lines&lt;/code&gt; &lt;/a&gt;, exposed in the same.&lt;/p&gt;

&lt;p&gt;Let’s start with the width. Our formula is pretty simple: subtract the width of the popup from the total columns in the editor (the width), and divide that by two to get the midway point. We need to subtract the popup’s width, since it would be pushed too far to the right without compensating for the space it takes up. We’ll finish by wrapping the whole expression in the Lua built-in &lt;code&gt;math.min&lt;/code&gt;, since &lt;code&gt;col&lt;/code&gt; expects whole numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll do something almost identical for &lt;code&gt;row&lt;/code&gt; (aka height), but instead of using &lt;code&gt;vim.o.columns&lt;/code&gt;, we’ll use &lt;code&gt;vim.o.lines&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we’re also adding an extra subtraction by one. This is because &lt;code&gt;vim.o.lines&lt;/code&gt; returns the total lines in the current window,&lt;em&gt;including&lt;/em&gt; the status line and the message area. That’s an extra two lines to account for. Since we want to center the popup vertically, to find how much to compensate by, we divide the extra lines by two, giving us one to subtract.&lt;/p&gt;

&lt;p&gt;Our function should now look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking over this code, there’s some repetition causing maintenance overhead: we’re writing literals for the width and height twice. We’ll need to change these values soon, so let’s refactor to use local variables for these values. Add a variable for &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;at the top of the &lt;code&gt;FuzzySearch&lt;/code&gt; function, since we’ll want them to be available throughout the scope. Our code should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you test this code, you’ll get something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fbasic-centered-floating-window.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fbasic-centered-floating-window.png" alt="Basic centered floating window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not much to look at, but at least it’s centered. But why is it only one line high, instead of five? Well, it actually is five lines high, but we can’t tell because our window has no outline style or contents. Let’s fix the former, then move on to the latter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling the window ¶
&lt;/h3&gt;

&lt;p&gt;Floating window styles are controlled by two properties, &lt;code&gt;style&lt;/code&gt; and &lt;code&gt;border&lt;/code&gt;. As of this writing, &lt;code&gt;style&lt;/code&gt; only has one valid value: &lt;code&gt;"minimal"&lt;/code&gt;. Fortunately, this option disables lots of inappropriate UI configurations for our search window, such as &lt;code&gt;number&lt;/code&gt; and &lt;code&gt;spell&lt;/code&gt; (see the docs for the full list).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;border&lt;/code&gt; has several built in options, as well as an option to define your own border characters (this is what Telescope does). Feel free to play around with the options, but for the purpose of this guide we’ll be using &lt;code&gt;"shadow"&lt;/code&gt;. I like this style because it’s visually uncluttered, and makes clear that this window is “above” others.&lt;/p&gt;

&lt;p&gt;While it’s not styling, let’s take a moment here to set the&lt;code&gt;noautocmd&lt;/code&gt; option to &lt;code&gt;true&lt;/code&gt;. This disables buffer events for the window, since we won’t be using them and it’s a good practice to limit the scope of our programs as much as sensible. Feel free to set this to&lt;code&gt;false&lt;/code&gt; later if you do end up using these methods.&lt;/p&gt;

&lt;p&gt;Our function should now look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'minimal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shadow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;noautocmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test this code and you should get something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fstyled-floating-window.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fstyled-floating-window.png" alt="Styled floating window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking good. Or, at least like a stylish postage stamp. Alright, let’s move on to the contents of the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching a fzy terminal ¶
&lt;/h2&gt;

&lt;p&gt;There are several ways Neovim offers for creating a new terminal instance, but we’ll be using the &lt;a href="https://neovim.io/doc/user/eval.html#termopen%28%29" rel="noopener noreferrer"&gt;&lt;code&gt;termopen()&lt;/code&gt; &lt;/a&gt;function, since it offers the most API control.&lt;/p&gt;

&lt;p&gt;We can ask it to provide a “standard” interactive terminal session, or to launch running a specific command. We’ll call it after our floating window setup code, using a basic command to gather files for &lt;code&gt;fzy&lt;/code&gt;to search, taken from their documentation, that should work on most systems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;termopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'find . -type f | fzy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;find&lt;/code&gt; command will grab every regular file in your current directory tree, and pass it to &lt;code&gt;fzy&lt;/code&gt;. Testing this code will produce a result similar to this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fcramped-find-fzy-search.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fcramped-find-fzy-search.png" alt="Cramped results from fzy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hooray! You should be able to search for a file, move up and down in the list via C-n and C-p, and select a file withEnter. However, you may be noticing some &lt;em&gt;slight&lt;/em&gt; issues.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The window is too small to see the results properly.&lt;/li&gt;
&lt;li&gt;Depending on your settings, you might not be in &lt;a href="https://neovim.io/doc/user/intro.html#Terminal-mode" rel="noopener noreferrer"&gt;terminal mode &lt;/a&gt;automatically when you enter the window, causing you to have to type i before you can search.&lt;/li&gt;
&lt;li&gt;Selecting a file produces a &lt;code&gt;[Process exited 0]&lt;/code&gt; message, making you press Enter again before continuing.&lt;/li&gt;
&lt;li&gt;Selecting a result does not open it!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Solving the second issue is dead simple: we call&lt;a href="https://neovim.io/doc/user/insert.html#:startinsert" rel="noopener noreferrer"&gt;startinsert &lt;/a&gt;before running &lt;code&gt;termopen()&lt;/code&gt; via&lt;a href="https://neovim.io/doc/user/api.html#nvim_command%28%29" rel="noopener noreferrer"&gt;&lt;code&gt;nvim_command&lt;/code&gt; &lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'startinsert'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll address each of the other issues, but let’s tackle the window size first, so we can better see what we’re doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic width and height ¶
&lt;/h3&gt;

&lt;p&gt;Alright, back to window sizing. We can improve the display by taking full advantage of the amount of space we have available to us. Since we already re-factored our &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; to single variables, we simply modify them where they are declared.&lt;/p&gt;

&lt;p&gt;Wouldn’t it be nice to stretch the width of the popup window to however large the Neovim instance is? Easy. We change the &lt;code&gt;width&lt;/code&gt; variable to equal &lt;code&gt;vim.o.columns&lt;/code&gt;, minus four. The number four is arbitrary; it gives two columns of space between the edge of the Neovim instance and the popup window, which feels right to me. Feel free to experiment with your own values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For setting the height, we want to show all the results that &lt;code&gt;fzy&lt;/code&gt;shows, or, in other words, we want our popup window to be as tall as the&lt;code&gt;fzy&lt;/code&gt; output. &lt;code&gt;fzy&lt;/code&gt; defaults to displaying ten search results at a time. This number can be controlled via the &lt;code&gt;--lines&lt;/code&gt; option, but changing that will be left as an exorcise for the reader. For now, we’ll redefine&lt;code&gt;height&lt;/code&gt; to be equal to &lt;code&gt;11&lt;/code&gt;, which is the default 10 results &lt;code&gt;fzy&lt;/code&gt;displays, plus an extra line for the search prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have an adaptive display window that shows our searches more clearly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fdynamic-window.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.chrisdeluca.me%2Farticle%2Fdiy-neovim-fzy-search%2Fimages%2Fdynamic-window.png" alt="Dynamic window width"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what happens on very large screens? Our window will stretch all the way across, packing the results at the left, and wasting space on the right. We can spend a moment fixing this by setting a max width for the window. The window will still center, so the eye won’t have to travel all the way to the edge to see results. The standard max line length for Vim is a sensible 80 columns, so we’ll stick to that for our window.&lt;/p&gt;

&lt;p&gt;Since we’re subtracting four from the total width, and we want to trigger the max &lt;em&gt;after&lt;/em&gt; we would naturally reach 80 columns, we’ll set the width at 85 columns.&lt;/p&gt;

&lt;p&gt;After our local variable declarations, we’ll add our conditional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the entirety of our function should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'minimal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shadow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;noautocmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;termopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'find . -type f | fzy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s move on to solving the third and fourth problems mentioned above—not actually being able to open the file searched for!&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening the searched for file ¶
&lt;/h3&gt;

&lt;p&gt;We want to perform an action—edit a file—when the terminal process for &lt;code&gt;fzy&lt;/code&gt; exits, which happens after the file is selected. We know from the &lt;a href="https://manpages.ubuntu.com/manpages/bionic/man1/fzy.1.html" rel="noopener noreferrer"&gt;&lt;code&gt;fzy&lt;/code&gt; man page &lt;/a&gt;that on exit &lt;q&gt;the currently selected&lt;br&gt;
item is printed to stdout&lt;/q&gt;, which is how we can detect which file is selected.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;termopen()&lt;/code&gt; function takes a table of event-driven callbacks as it’s second argument. We’ll be using the appropriately named &lt;code&gt;on_exit&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;termopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'find . -type f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;on_exit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;-- code goes here.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s get rid of the extra Enter press. Inside the &lt;code&gt;on_exit&lt;/code&gt;callback, we’ll call &lt;a href="https://neovim.io/doc/user/windows.html#:bdelete" rel="noopener noreferrer"&gt;&lt;code&gt;bdelete&lt;/code&gt; &lt;/a&gt;, meaning that once the terminal process exits, we’ll automatically delete the buffer. We’ll add the &lt;code&gt;!&lt;/code&gt; option, which will delete the buffer even if there are changes to it. This buffer should never have meaningful changes, so we never want that safety (otherwise, if there were changes, &lt;code&gt;bdelete&lt;/code&gt; would produce an error).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bdelete!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you test the function, the popup window should immediately dismiss after a file is selected. Excellent!&lt;/p&gt;

&lt;p&gt;Now we can move on to opening the file searched for. We know that &lt;code&gt;fzy&lt;/code&gt;prints the path to the selected file to &lt;code&gt;{stdout}&lt;/code&gt;. Maybe there’s an argument that Neovim passes &lt;code&gt;{stdout}&lt;/code&gt; to the terminal event callbacks? However, the &lt;a href="https://neovim.io/doc/user/job_control.html#on_exit" rel="noopener noreferrer"&gt;&lt;code&gt;on_exit&lt;/code&gt; &lt;/a&gt;callback only receives the &lt;a href="https://neovim.io/doc/user/job_control.html#job-id" rel="noopener noreferrer"&gt;job id &lt;/a&gt;, the exit code, and the event type, which in this case is always “exit”.&lt;/p&gt;

&lt;p&gt;There &lt;em&gt;must&lt;/em&gt; be a better way to solve this, but how I’ve figured it out is to write the contents of &lt;code&gt;{stdout}&lt;/code&gt; to a file as part of the &lt;code&gt;fzy&lt;/code&gt; pipeline, then read the file contents back in the&lt;code&gt;on_exit&lt;/code&gt; function. If you know of a better method, hit me up &lt;a href="https://twitter.com/chrisjohndeluca" rel="noopener noreferrer"&gt;on Twitter &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since the file we’re creating is totally throw-away, you could say temporary, we’ll use Neovim’s &lt;a href="https://neovim.io/doc/user/eval.html#tempname%28%29" rel="noopener noreferrer"&gt;&lt;code&gt;tempname()&lt;/code&gt; &lt;/a&gt;function to generate a unique temporary file name in a clean path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tempname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can save the output &lt;code&gt;fzy&lt;/code&gt; (which is &lt;code&gt;{stdout}&lt;/code&gt;) to our file with simple &lt;a href="http://www.linfo.org/redirection.html" rel="noopener noreferrer"&gt;Unix redirection &lt;/a&gt;and &lt;a href="https://www.lua.org/manual/5.1/manual.html#2.5.4" rel="noopener noreferrer"&gt;Lua concatenation &lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="s1"&gt;'find . -type f | fzy &amp;gt; '&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back inside our &lt;code&gt;on_exit&lt;/code&gt; callback function, and after our &lt;code&gt;bdelete&lt;/code&gt;call, is where we can access the file we wrote. Lua provides a robust&lt;a href="https://www.lua.org/manual/5.1/manual.html#5.7" rel="noopener noreferrer"&gt;filesystem API &lt;/a&gt;which we can use to open a stream to the file and read the contents into a variable. We’ll open the file stream as read only, keeping the principle of only asking for what we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;io.open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*all'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should also clean up after ourselves, removing the temporary file from disk and closing the file stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nb"&gt;os.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the file path stored in the &lt;code&gt;stdout&lt;/code&gt; variable; we can use the &lt;code&gt;nvim_command&lt;/code&gt; Neovim API method to &lt;code&gt;:edit&lt;/code&gt; it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'edit '&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our whole function should now look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'minimal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shadow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;noautocmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tempname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;termopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'find . -type f | fzy &amp;gt; '&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;on_exit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bdelete!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;io.open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*all'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;os.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'edit '&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the function; selecting a file should open it. Yay! We have a fully working solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the search command ¶
&lt;/h2&gt;

&lt;p&gt;Wouldn’t it be nice to be able to access our function outside of our&lt;code&gt;fuzzy-search.lua&lt;/code&gt; file? Say, in our &lt;code&gt;init.vim&lt;/code&gt; or &lt;code&gt;init.lua&lt;/code&gt; file?&lt;/p&gt;

&lt;p&gt;Lua includes a simple yet powerful &lt;a href="https://www.lua.org/manual/5.1/manual.html#5.3" rel="noopener noreferrer"&gt;module system &lt;/a&gt;, which we can leverage with only a few changes to our file.&lt;/p&gt;

&lt;p&gt;All we need to do is &lt;code&gt;return&lt;/code&gt; our function, and that will expose it to &lt;code&gt;require&lt;/code&gt; statements. However, to make it possible to add further exportable functions to this file in the future, and to adhere to convention, we’ll add our function to a table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;-- all our code.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We name the returned variable &lt;code&gt;M&lt;/code&gt;, again, to follow convention.&lt;/p&gt;

&lt;p&gt;This adds &lt;code&gt;fuzzy-search&lt;/code&gt; as a module to the Neovim environment. In a Lua file within the Neovim context, we could add our function to the environment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="s1"&gt;'fuzzy-search'&lt;/span&gt;
&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there’s no &lt;code&gt;.lua&lt;/code&gt; extension or leading &lt;code&gt;lua&lt;/code&gt; directory name in the &lt;code&gt;require&lt;/code&gt;—Neovim/Lua handles this for us so we don’t have to type all that.&lt;/p&gt;

&lt;p&gt;Now, in our &lt;code&gt;init.vim&lt;/code&gt; or &lt;code&gt;init.lua&lt;/code&gt; file, we can create a mapping to this function by requiring our search file inline, and parsing it with the built-in &lt;a href="https://neovim.io/doc/user/lua.html#:lua" rel="noopener noreferrer"&gt;&lt;code&gt;lua&lt;/code&gt; &lt;/a&gt;command.&lt;/p&gt;

&lt;p&gt;Say we wanted to map &amp;lt;leader&amp;gt;f, we would add, for &lt;code&gt;init.vim&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;f&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;cmd&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;lua&lt;/span&gt; require&lt;span class="s1"&gt;'fuzzy-search'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;FuzzySearch&lt;span class="p"&gt;()&amp;lt;&lt;/span&gt;CR&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for &lt;code&gt;init.lua&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;leader&amp;gt;f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua require"fuzzy-search".FuzzySearch()&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The final script ¶
&lt;/h2&gt;

&lt;p&gt;We did it. Here’s our completed code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- ~/.config/nvim/lua/fuzzy-search.lua&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FuzzySearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_open_win&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_buf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'minimal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shadow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;noautocmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;math.min&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tempname&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;termopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'find . -type f | fzy &amp;gt; '&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;on_exit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bdelete!'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;io.open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*all'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;os.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'edit '&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="//./documents/fuzzy-search.lua"&gt;Download the search code.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvement ideas ¶
&lt;/h2&gt;

&lt;p&gt;This script is just a starting point. Here’s some ideas for improvements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow mappings to specify how they want to open the file (split/vsplit/tab/etc).&lt;/li&gt;
&lt;li&gt;Allow mappings to change the file gathering command, e.g. &lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;&lt;code&gt;fd&lt;/code&gt; &lt;/a&gt;or my personal favoriate, &lt;a href="https://git-scm.com/docs/git-ls-files" rel="noopener noreferrer"&gt;&lt;code&gt;git ls-files&lt;/code&gt; &lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Allow mappings to change the number of results returned.&lt;/li&gt;
&lt;li&gt;Get rid of that file write!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I implemented some of these in my &lt;a href="https://github.com/bronzehedwick/dotfiles/blob/main/neovim/.config/nvim/lua/fuzzy-search.lua" rel="noopener noreferrer"&gt;own dotfiles &lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up ¶
&lt;/h2&gt;

&lt;p&gt;That’s it! Thanks for reading.&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>lua</category>
    </item>
    <item>
      <title>Hand Drawn SVGs</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Sun, 07 Nov 2021 19:11:50 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/hand-drawn-svgs-2bcn</link>
      <guid>https://forem.com/bronzehedwick/hand-drawn-svgs-2bcn</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/hand-drawn-svgs/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve been trying to find a good workflow for drawing vector artwork that has the spontaneity and roughness of hand drawn images.&lt;/p&gt;

&lt;h3&gt;TL;DR&lt;/h3&gt;

&lt;p&gt;If you're just interested in my process, and not the backstory, skip to the workflow&lt;/p&gt;

&lt;p&gt;Since my digital medium of choice is the web, that means SVG is the only output format in town. Which is perfect, since like everybody, I never liked choice anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preamble Ramble ¶
&lt;/h2&gt;

&lt;p&gt;There’s some &lt;a href="https://heredragonsabound.blogspot.com/2020/02/creating-pencil-effect-in-svg.html"&gt;interesting experiments &lt;/a&gt;with giving programmatically drawn SVGs a hand drawn quality, but I want to have the artistic freedoms that come with actually drawing the images by hand, not just the feel of being hand drawn.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y39SzJtI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/programmatic-mountains.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y39SzJtI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/programmatic-mountains.png" alt="Mountains drawn programmatically, with a hand drawn feel." width="378" height="253"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;These mountains were not drawn by hand.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I need a vector drawing program, but that’s where everything falls apart. In my experience, there’s no drawing tool that combines the expressiveness of drawing freehand that also natively handles SVG, or even reliably outputs it.&lt;/p&gt;

&lt;p&gt;Sure, &lt;a href="https://inkscape.org/"&gt;Inkscape &lt;/a&gt;and &lt;a href="https://www.adobe.com/products/illustrator.html"&gt;Illustrator &lt;/a&gt;provide a lot of power, but they feel more like drafting tools, not drawing tools. I want the experience of drawing something free-hand, without constraints or planning or thinking about the geometry.&lt;/p&gt;

&lt;p&gt;The situation has me pining for the bad old days of Macromedia Flash, which, everything else aside, still had the best vector drawing tool for my money.&lt;/p&gt;

&lt;p&gt;You could draw all the regular geometry that modern apps offer, but you could also draw free hand. If you drew a closed shape free hand, the tool would recognize the shape, allowing you to apply transforms or color it in with a click.&lt;/p&gt;

&lt;p&gt;Yet as much as I look back at the time of Flash with rose-colored glasses, it also sucked. For one, you had to draw with a mouse, since this was before the days of good, relatively cheap touch screens. That made the “free-hand” not quite free; mouse-hand, more like. The lines didn’t look the same.&lt;/p&gt;

&lt;p&gt;These days I have access to a iPad with an Apple Pencil, so I could draw my lines there, then convert them to SVG. In fact, that feature is built into several popular drawing applications.&lt;/p&gt;

&lt;p&gt;Still, I was curious about a &lt;em&gt;fully&lt;/em&gt; hand drawn approach. I’m also a software developer, so I decided to try a workflow that at the same time involved a lot less digital technology &lt;em&gt;and&lt;/em&gt; more command line. Both sides were happy.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Workflow ¶
&lt;/h2&gt;

&lt;p&gt;I drew my image traditionally, with a pencil on paper, in black and white, took a picture of it, did some image processing, converted that to SVG, then imported it into an SVG editing application for coloring.&lt;/p&gt;

&lt;p&gt;So far, I don’t hate this process.&lt;/p&gt;

&lt;p&gt;I started by drawing a lot of doodles using a drawing pencil and a “light touch”. By light touch, I mean that I tried not to bore down on the pencil stroke, and keep my lines light and “exploratory”, as my wife put it. This let me keep figuring out the drawing as I drew, without being married to anything other than my wife.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jxY_G3NM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/pencil.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jxY_G3NM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/pencil.jpg" alt="Artist's pencil" width="600" height="375"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is the pencil I used.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I kept my doodles unrelated, just drawing whatever I felt my lines were creating already.&lt;/p&gt;

&lt;p&gt;Once I was happy with that, I used one of my wife’s fancy inking pens to make a clear, dark line over my pencil strokes. The pencil was still visible underneath the ink, but there was no mistaking where the “real” line was.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5plLBG9D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/pen.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5plLBG9D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/pen.jpg" alt="Artist's pen" width="600" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is the inking pen I used.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I took that drawing, and photographed it on my iPhone 7 camera with as bright light as I could manage. In fact, it’s the same image (at a higher resolution) as seen below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zCnZ13dH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/initial-scan.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zCnZ13dH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/initial-scan.jpg" alt="iPhone photo of my drawing" width="880" height="660"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;You can see the pencil lines below the ink lines, and the imperfections of the paper.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I took that image and copied it to my Mac Mini, then into&lt;a href="https://flyingmeat.com/acorn/"&gt;Acorn &lt;/a&gt;, which is a streamlined, Mac-centric version of Photoshop that I find easier to use. I adjusted the color levels to omit as many of the smudgy greys from the paper and the pencil strokes as possible, while keeping the ink lines relatively dark.&lt;/p&gt;

&lt;p&gt;Then, I switched the graphic to black and white (as distinct from gray scale; every color is either black or white). I played with those “sharpness” levels until all the remaining pencil lines were white, and the ink lines were pure black. I saved the result, below, as a &lt;code&gt;.bmp&lt;/code&gt;file, for reasons that will become momentarily apparent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bf2rXjR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/black-and-white-doodle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bf2rXjR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/black-and-white-doodle.png" alt="Black and white version of the image" width="800" height="604"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Look at all those clean lines.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Remember that big &lt;code&gt;.bmp&lt;/code&gt; mystery from just now? I mean, why save it as such an archaic file type? Because it’s an uncompressed image type, so no data is lost, but more importantly, it’s what &lt;a href="https://en.wikipedia.org/wiki/Potrace"&gt;Potrace &lt;/a&gt;, the bitmap to vector conversion tool, expects (it can also ingest &lt;code&gt;.pnm&lt;/code&gt;files as well, but honestly I have no idea what those are and I haven’t been bothered to look them up).&lt;/p&gt;

&lt;p&gt;Potrace is an open source tool used at the command line, that is also part of other commercial and non-commercial software, such as the afore mentioned Inkscape. I actively enjoy the command line, so I don’t mind the “unglamorous” interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;potrace --backend svg my-image.bmp --output my-image.svg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I told the program to output SVGs (it can also do EPS and Postscript), and it worked flawlessly. It only operates on black and white images, but I already knew that so everything was going just fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VvPWCwnd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/black-and-white-doodle.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VvPWCwnd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/black-and-white-doodle.svg" alt="My black and white doodle converted to SVG" width="880" height="665"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same black and white doodle, converted from a bitmap to a vector. Compare to the image above; more crisp, right?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next step, color. I opened my new SVG in &lt;a href="https://boxy-svg.com/"&gt;Boxy SVG &lt;/a&gt;, which is a simple program that natively handles SVGs.&lt;/p&gt;

&lt;p&gt;Since I had lots of doodle “subjects” in my initial drawing, I decided to focus on the character in the upper left with the cape, which I called “Super Wisp”.&lt;/p&gt;

&lt;p&gt;If this was Flash, I could have just used the paint bucket tool to fill in each contiguous shape. However, with all the SVG tools I’ve used, the shape has to use &lt;code&gt;&amp;lt;polygon&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ellipse&amp;gt;&lt;/code&gt;, or the like under the hood to fill in shapes with a click (the programs just attach a &lt;code&gt;fill&lt;/code&gt;property to the element). Auto-generated SVG code, like the output of Potrace, almost always draw with simple &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; elements, which are hard if not impossible to fill with color.&lt;/p&gt;

&lt;p&gt;Instead, my plan was to draw shapes of the same dimensions as the lines it is meant to fill in, give them a background color, then move them behind the lines in the layer view.&lt;/p&gt;

&lt;p&gt;I started coloring by making a green ellipse for the head. The head I drew was not a perfect ellipse, so the background color poked out on the edges. However, I felt it added a certain rough feel, like in cheap comics where the color printing is not that exact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q1o7ZN-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/head-color.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q1o7ZN-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/head-color.png" alt="Coloring in Super Wisp's head." width="800" height="709"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Also pictured: the same “overflow” technique for the eyes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The sloppy, overflowing color look was all well and good, but at the risk of being anti-creative, could I color &lt;em&gt;inside&lt;/em&gt; the lines?&lt;/p&gt;

&lt;p&gt;Turns out: yes. I used the polygon tool to draw points in an angular version of the shape I wanted to color in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nNYR5Xt1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/coloring-the-cape.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nNYR5Xt1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/coloring-the-cape.png" alt="Coloring in the cape with the polygon tool." width="663" height="628"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Half way through drawing a “cape” polygon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once I put the finished shape behind the lines, the lines obscured the pointy edges of the polygon, creating the illusion of continuous color.&lt;/p&gt;

&lt;p&gt;Unfortunately, my results weren’t perfect on the first drawing—it was hard to tell if my polygon edge would fall exactly on the line, especially near the top—as illustrated, below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b1X7ML6L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/cape-imperfection.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b1X7ML6L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/cape-imperfection.png" alt="The cape color goes outside the line" width="245" height="152"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Sure, I could go cheap color look again here, but I wanted to see if I could get an more exact fill.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, Boxy SVG provides excellent shape transform tools. I was able to adjust geometry points until the shape fell inside the lines how I wanted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--48MP8V8s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/transform-cape.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--48MP8V8s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/transform-cape.png" alt="Transforming the cape shape with control points." width="800" height="871"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Each dot is a control point for the polygon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once I was happy with my color, I selected only my colored-in Super Wisp character, and exported it. For a drawing with the kind of irregularities of hand drawn, I was expecting a semi-large file size, but it ending up weighing in at only &lt;code&gt;64k&lt;/code&gt; on disk. This is before optimizations and minification, so the raw SVG code looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;svg viewBox="444.783 273.522 749.717 1341.237" xmlns="http://www.w3.org/2000/svg"&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;title&amp;gt;Super Wisp&amp;lt;/title&amp;gt;
&amp;lt;g&amp;gt;
&amp;lt;title&amp;gt;Color&amp;lt;/title&amp;gt;
&amp;lt;polygon style="stroke: rgb(0, 0, 0); fill: rgb(248, 106, 106);" points="601.655 994.655 551.522 1042.12 540.507 1115.09 559.784 1190.82 630.653 1268.49 761.935 1345.18 883.477 1369.06 954.659 1425.66 971.182 1500.33 960.166 1544.39 984.7 1600.03 1075.78 1605.45 1156.2 1557.99 1190.15 1467.87 1175 1431.5 1148.84 1372.3 1162.53 1287.74 1145.36 1232.1 1072.32 1233.48 984.195 1260.366 912.535 1209.34 870.519 1125.57 886.312 1071.31 909.719 1036.88 900.081 1017.6 798.189 1001.08 765.143 966.659"/&amp;gt;
&amp;lt;!-- The real code keeps going, but one can only stare at so much XML. --&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran the file through &lt;a href="https://github.com/RazrFalcon/svgcleaner"&gt;Svgcleaner &lt;/a&gt;, and it cut the file size by more than half, down to &lt;code&gt;31k&lt;/code&gt;! Now the code looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svg viewBox="444.783 273.522 749.717 1341.237" xmlns="http://www.w3.org/2000/svg"&amp;gt;&amp;lt;path d="m601.655 994.655-50.133 47.465-11.015 72.97 19.277 75.73 70.869 77.67 131.282 76.69 121.542 23.88 71.182 56.6 16.523 74.67-11.016 44.06 24.534 55.64 91.08 5.42 80.42-47.46 33.95-90.12-15.15-36.37-26.16-59.2 13.69-84.56-17.17-55.64-73.04 1.38-88.125 26.886-71.66-51.026-42.016-83.77 15.793-54.26 23.407-34.43-9.638-19.28-101.892-16.52-33.046-34.421z" fill="#f86a6a" stroke="#000"/&amp;gt;
&amp;lt;!-- Enough of this; the only thing worse than starting at too much XML is starting at too much minified XML. --&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here it is, the finished SVG, embedded directly into this page, in all it’s sloppy glory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lraY9qIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/Super%2520Wisp.min.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lraY9qIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/hand-drawn-svgs/images/Super%2520Wisp.min.svg" alt="The finished Super Wisp drawing." width="749" height="1341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s still lots of imperfections, which really stretch the limits of what can be called artistic roughness, which are especially noticeable at larger screen sizes. Yet ultimately, I found the test successful. To me, the image has charm and personality, and as my psychologist warned, came directly from me.&lt;/p&gt;

&lt;p&gt;The workflow isn’t perfect, but it’s very workable. The whole process took me only a couple of hours, and this was my first time doing it. I’m excited to keep exploring.&lt;/p&gt;

</description>
      <category>art</category>
    </item>
    <item>
      <title>Fixing GPG Yubikey integration on macOS Big Sur</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Sat, 28 Nov 2020 18:47:48 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/fixing-gpg-yubikey-integration-on-macos-big-sur-53f5</link>
      <guid>https://forem.com/bronzehedwick/fixing-gpg-yubikey-integration-on-macos-big-sur-53f5</guid>
      <description>&lt;p&gt;I had some trouble after upgrading GPGTools to version 2020.2 on macOS Big Sur, where it would ignore my Yubikey smart card and I couldn’t unlock my stuff.&lt;/p&gt;

&lt;p&gt;Every time I tried to use gpg (Yubikey inserted), I would get this&lt;br&gt;
error:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg: decryption failed: No secret key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This sent me into a wild rage, and after spending far too much time&lt;br&gt;
trying to debug with no results, I switched tactics; remove GPGTools and&lt;br&gt;
install gpg myself. While it's still early days, and I am by no means a&lt;br&gt;
gpg expert (who is?), everything seems to be working fine.&lt;/p&gt;

&lt;p&gt;Here's how I did it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Uninstall GPGTools
&lt;/h2&gt;

&lt;p&gt;I downloaded the uninstaller from the GPGTools website; that's right, it&lt;br&gt;
is &lt;em&gt;not&lt;/em&gt; included in the standard GPGTools installation. Another reason&lt;br&gt;
to ditch it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gpgtools.tenderapp.com/kb/faq/uninstall-gpg-suite"&gt;https://gpgtools.tenderapp.com/kb/faq/uninstall-gpg-suite&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Install GPG
&lt;/h2&gt;

&lt;p&gt;I used homebrew to install the required packages.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install gpg pinentry-mac # pinentry-mac is needed for smart cards.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;I also added the two packages to my &lt;a href="https://dev.tobrewfile"&gt;Brewfile&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/Brewfile b/Brewfile
index 683e138..9b0d988 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/Brewfile
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/Brewfile
&lt;/span&gt;&lt;span class="p"&gt;@@ -13,6 +13,7 @@&lt;/span&gt; brew "fzy"
 brew "git"
 brew "git-delta"
 brew "git-standup"
&lt;span class="gi"&gt;+brew "gpg"
&lt;/span&gt; brew "hugo"
 brew "imagemagick"
 brew "isync"
&lt;span class="p"&gt;@@ -27,6 +28,7 @@&lt;/span&gt; brew "pandoc"
 brew "par"
 brew "pass"
 brew "pianobar"
&lt;span class="gi"&gt;+brew "pinentry-mac"
&lt;/span&gt; brew "rename"
 brew "ripgrep"
 brew "rust"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure GPG
&lt;/h2&gt;

&lt;p&gt;The gpg installation added a &lt;code&gt;.gnupg/&lt;/code&gt; configuration directory to my&lt;br&gt;
home folder. After some research, I added a few lines to &lt;code&gt;gpg.conf&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;gpg-agent.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.gnupg/gpg.conf&lt;/span&gt;
ask-cert-level
use-agent
auto-key-retrieve
no-emit-version
default-key D81A4957BAF06BCA6E060EE5461C015E032EF9CB &lt;span class="c"&gt;# use your key&lt;/span&gt;

&lt;span class="c"&gt;# ~/.gnupg/gpg-agent.conf&lt;/span&gt;
pinentry-program /usr/local/bin/pinentry-mac
default-cache-ttl 600
max-cache-ttl 7200
debug-level basic
log-file &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.gnupg/gpg-agent.log &lt;span class="c"&gt;# helpful for debugging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I was making progress, but when I tried to use gpg I would get this&lt;br&gt;
error:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg: OpenPGP card not available: No SmartCard daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This one took some time to figure out. I checked&lt;br&gt;
my homebrew installation, and scdaemon existed at&lt;br&gt;
&lt;code&gt;/usr/local/Cellar/gnupg/2.2.25/libexec/scdaemon&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I eventually figured out I needed a scdaemon configuration file, and I&lt;br&gt;
needed to pass in the name of my smart card there.&lt;/p&gt;

&lt;p&gt;macOS comes with a command line tool for testing smart cards (PC/SC),&lt;br&gt;
which I used to get the machine name of my smart card.&lt;/p&gt;

&lt;p&gt;I inserted my Yubikey and ran &lt;code&gt;pcsctest&lt;/code&gt;, which gave me this output:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MUSCLE PC/SC Lite Test Program

Testing SCardEstablishContext    : Command successful.
Testing SCardGetStatusChange
Please insert a working reader   : Command successful.
Testing SCardListReaders         : Command successful.
Reader 01: Yubico YubiKey OTP+FIDO+CCID
Enter the reader number          :
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The "Reader" line is what we're interested in. I copied the name of my&lt;br&gt;
smart card, killed &lt;code&gt;pcsctest&lt;/code&gt; with a Ctrl-c, and pasted to a&lt;br&gt;
file called &lt;code&gt;scdaemon.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.gnupg/scdaemon.conf&lt;/span&gt;
reader-port &lt;span class="s2"&gt;"Yubico YubiKey OTP+FIDO+CCID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finishing up
&lt;/h2&gt;

&lt;p&gt;I had to restart gpg agent before my changed would take effect.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;killall gpg-agent
gpg-agent --daemon --homedir $HOME/.gnupg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it, things are working for me again, and I got to replace a&lt;br&gt;
large dependency (GPGTools) with a slightly smaller one (GPG).&lt;/p&gt;

&lt;h3&gt;
  
  
  Other resources
&lt;/h3&gt;

&lt;p&gt;Some links I found helpful in my journey to figuring this out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/wes/how2-using-gpg-on-macos-without-gpgtools-428f"&gt;https://dev.to/wes/how2-using-gpg-on-macos-without-gpgtools-428f&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://miki725.com/crypto/2018/12/23/gpg-mac-yubikey.html"&gt;https://miki725.com/crypto/2018/12/23/gpg-mac-yubikey.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.programster.org/yubikey-link-with-gpg"&gt;https://blog.programster.org/yubikey-link-with-gpg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gpg</category>
      <category>yubikey</category>
      <category>macos</category>
    </item>
    <item>
      <title>Painfully Rendering a Simple Bar Chart with Nivo</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Sun, 15 Nov 2020 21:56:57 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/painfully-rendering-a-simple-bar-chart-with-nivo-877</link>
      <guid>https://forem.com/bronzehedwick/painfully-rendering-a-simple-bar-chart-with-nivo-877</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve been working on a graphing project for the &lt;a href="https://astoria.digital/"&gt;Astoria Digital&lt;/a&gt; volunteer group in collaboration with &lt;a href="https://www.muckrock.com/"&gt;Muckrock&lt;/a&gt;. The app will visualize data around New York’s 50a police disciplinary record requests.&lt;/p&gt;

&lt;p&gt;My first task was a proof of concept - to put some very basic data in a bar chart. The data has a single axis: how much each request cost. We only care about unique costs for the purposes of this visualization, so the list ended up being only 19 values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
"$47,504.00"
"$30,815.00"
"$20,510.00"
"$5,000.00"
"$846.16"
"$250.00"
"$244.66"
"$200.00"
"$56.15"
"$36.00"
"$28.00"
"$27.25"
"$19.39"
"$17.50"
"$14.75"
"$13.25"
"$8.75"
"$7.50"
"$3.00"
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to show each value in relation to each other with a good old fashioned bar chart. Since the project was already using &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;, I decided to go with &lt;a href="https://nivo.rocks/"&gt;nivo&lt;/a&gt; as the graphing library, which uses &lt;a href="https://d3js.org/"&gt;d3&lt;/a&gt; under the hood.&lt;/p&gt;

&lt;p&gt;That’s when things started to get interesting, and by interesting I mean unexpected, and by unexpected I mean annoying.&lt;/p&gt;

&lt;p&gt;Nivo has lots of documentation, with a really slick display, interactive widgets and lots of examples on how to solve complex problems. However, noticeably lacking is much documentation around how to solve simple problems, like implementing a dead simple bar chart. Equally missing is a robust getting started document, or a explanation of the architecture of the library, key concepts, assumptions, and the like.&lt;/p&gt;

&lt;p&gt;Irritating, but fine. I’ve been doing web development long enough not to be scared away. I dug in, and while it took me longer than I would have liked to piece together what I actually needed for my little bar chart, I had a working widget.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Bar&lt;/span&gt;
  &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;horizontal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, working was a bit of an overstatement. While it rendered, it looked, well, &lt;em&gt;off&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a3hlSrwy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/before.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a3hlSrwy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/before.png" alt="" width="880" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Was my data wrong? I double and triple checked it, throwing &lt;code&gt;console.log&lt;/code&gt;s all over the place. Everything seemed fine.&lt;/p&gt;

&lt;p&gt;After some careful eyeballing, it seemed like the issue was not that the data was wrong, but that the display was being cut off.&lt;/p&gt;

&lt;p&gt;I checked the nivo documentation - maybe I missed a vital configuration? Some CSS file I needed to include? Where’s the option for “render correctly”?&lt;/p&gt;

&lt;p&gt;Unfortunately all I found was very fancy how-tos on how to render multi-colored bars with multi-layered data and lots of animations. Cool stuff, but not helpful here.&lt;/p&gt;

&lt;p&gt;I was starting to get frustrated. How hard could it be to render a dead simple bar chart?&lt;/p&gt;

&lt;p&gt;I tried extending the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on the chart, but that only made the SVG canvas bigger, leaving in tact the cut-off values.&lt;/p&gt;

&lt;p&gt;Finally, I broke down and started digging into nivo’s code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The work
&lt;/h2&gt;

&lt;p&gt;While I couldn’t find any documentation on it, maybe there was a CSS file I needed to include? I poked around in the SVG source that nivo generates, and noticed that the SVG elements did not include any classes or ids. That hinted that there was no default CSS file to include, but to check my assumption, I searched the entire node package for any CSS file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find ./node_modules/@nivo/ &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.css"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returned no results, so I could confidently cross off “forgot to include library CSS file” from my debugging checklist.&lt;/p&gt;

&lt;p&gt;If there was no external CSS library expected, the chart would have to be styled another way. SVGs have lots of styling effects via the use of properties; maybe something in there was amiss? Seemed like a stretch that there was a bug in the library for rendering such a simple graphic, but I didn’t have any better ideas, so I started combing through the SVG markup.&lt;/p&gt;

&lt;p&gt;I didn’t notice anything strange about the SVG properties, but I did notice lots of inline CSS styles. Okay, so that makes sense; the library needs more styling power than pure SVG, and since it isn’t using an external CSS source, it’s injecting the styles on each element directly.&lt;/p&gt;

&lt;p&gt;That made me challenge a core assumption I had been making: that the library was smart enough to fit the data given inside the SVG canvas. After checking the API documentation again with my new hypothesis goggles, I found the &lt;code&gt;margin&lt;/code&gt; property, with this description:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Chart margin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not exactly the clarification I was hoping for, but my new theory was that since the library only dealt in inline styles (seeing as another docs search found no method of passing in custom CSS class/id values) and values being cut off is essentially an offset issue, I figured that&lt;code&gt;margin&lt;/code&gt; sounded close enough to how I hoped the styles could change.&lt;/p&gt;

&lt;p&gt;I slapped that sucker on the component, and sure enough, it fixed the issue. My assumption was (unfortunately) correct; you have to explicitly set &lt;code&gt;margin&lt;/code&gt;s for your simple bar chart, otherwise nivo might not display it properly.&lt;/p&gt;

&lt;p&gt;This would have been really helpful to have outlined in a core concept document, and was rather disheartening, because it meant that component configuration is tied directly to the shape of the data, adding a maintenance headache. However, it worked, and that issue was for future Chris to deal with.&lt;/p&gt;

&lt;p&gt;Unfortunately, I wasn’t completely done. Since my chart was so simple, containing only one value column, I wanted to simplify the display. By default, the nivo chart outputs x and y axis labels, as well as a label overlaying the bar itself. This is great for complex, multi-axis data, but for a simple chart like this was entirely redundant, and also caused overlapping values because of the vast differentials between them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6XwmTkUh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/iteration-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6XwmTkUh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/iteration-1.png" alt="" width="880" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My ideal state was to hide the x and y axis displays, and just use the label overlaying the bar, since the data isn’t relational. However, nivo does not present an option for this. It does provide an option for hiding the overlay labels, so I went with that.&lt;/p&gt;

&lt;p&gt;That fixed the overlapping data issue, yet the y axis was still redundant and potentially confusing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KtaMaoPV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/iteration-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KtaMaoPV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/iteration-2.png" alt="" width="880" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, nivo offers no way to hide this display, so I turned to the dark arts: hacky CSS.&lt;/p&gt;

&lt;p&gt;Since nivo doesn’t supply unique identifiers for elements, nor does it supply a method to pass in your own, I resorted to this monstrosity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.headline__costs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nt"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;g&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;g&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eagle-eyed CSS-folk will have noticed that this code is brittle as hell; if the nivo library changes how it outputs it’s SVG markup, say, moving the y axis to be the first child instead of the second, this code will hide the wrong element.&lt;/p&gt;

&lt;p&gt;That being said, this works for now, and provides some real value by making the data visualization more clear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k5NWtxyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/final.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k5NWtxyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/rendering-simple-bar-chart-nivo/images/final.png" alt="" width="880" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that’s my journey with nivo so far; an impressive library with lots slick functionality, but some strange omissions around dynamically rendering data shapes and a lack of basic documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  In closing, nivo &lt;em&gt;does&lt;/em&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Render SVGs using only inline CSS&lt;/li&gt;
&lt;li&gt;Allow some style tweaks to that inline CSS via component properties&lt;/li&gt;
&lt;li&gt;Allows hiding overlay labels&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Nivo &lt;em&gt;does not&lt;/em&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automatically render chars so that all given data is visible (offsets must be manually set via the &lt;code&gt;margin&lt;/code&gt; property for each dataset)&lt;/li&gt;
&lt;li&gt;Provide getting started documentation aimed at new users&lt;/li&gt;
&lt;li&gt;Provide documentation on core concepts for how the library functions&lt;/li&gt;
&lt;li&gt;Make rendering a simple bar chart simple (oooh, 🌶️!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nivo</category>
    </item>
    <item>
      <title>PHP Boolean Casting Gotchas</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Mon, 19 Oct 2020 02:18:55 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/php-boolean-casting-gotchas-557i</link>
      <guid>https://forem.com/bronzehedwick/php-boolean-casting-gotchas-557i</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/php-boolean-casting-gotchas/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I find PHP’s boolean casting rules strange and hard to remember. I know I’m not alone.&lt;/p&gt;

&lt;p&gt;To help myself out, and anyone else that happens to stumble upon it, I wrote a commented script that demonstrates how each kind value is cast to booleans.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://www.chrisdeluca.me/article/php-boolean-casting-gotchas/boolean-casting.php"&gt;download the script&lt;/a&gt; to run it yourself, or view it below (or both!).&lt;/p&gt;

&lt;p&gt;You can also skip to the results of the script (aka, what booleans are cast to!)&lt;/p&gt;

&lt;p&gt;I added the comment “Uh oh!” where I thought the result was unexpected or inconsistent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;fooClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;do_foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, world"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$fooNull&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooFalse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooFalseyString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooTruthyString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooFalseyArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nv"&gt;$fooTruthyArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"everyone"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$fooFalseyNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooTruthyNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooTruthyObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;fooClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooFalseyObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$fooNegativeNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$fooNegativeZero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PHP Version: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;phpversion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Uh oh!&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; added after every logically unexpected result.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// NULL {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNull&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates null to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates null to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNull&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates null to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates null to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNull&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates null to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates null to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Unset variable {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooUnset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates an unset variable to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates unset variable to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooUnset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates an unset variable to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates unset variable to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooUnset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates an unset variable to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates unset variable to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// FALSE {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates false to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates false to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalse&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates false to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates false to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalse&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates false to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates false to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// TRUE {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTrue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates true to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates true to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTrue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates true to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates true to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTrue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates true to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates true to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Falsey string {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a falsey string to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a falsey string to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a falsey string to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a falsey string to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a falsey string to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a falsey string to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Truthy string {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a truthy string to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a truthy string to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a truthy string to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a truthy string to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a truthy string to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a truthy string to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Falsey array {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a falsey array to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a falsey array to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyArray&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a falsey array to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a falsey array to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyArray&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a falsey array to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a falsey array to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Truthy array {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Truthy array to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Truthy array to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyArray&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Truthy array to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Truthy array to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyArray&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Truthy array to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Truthy array to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Falsey number {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Falsey number to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Falsey number to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Falsey number to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Falsey number to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Falsey number to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Falsey number to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Truthy number {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Truthy number to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Truthy number to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Truthy number to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Truthy number to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Truthy number to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Truthy number to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Truthy object {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Truthy object to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Truthy object to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Truthy object to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Truthy object to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooTruthyObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Truthy object to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Truthy object to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Falsey object {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Falsey object to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Falsey object to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Falsey object to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Falsey object to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooFalseyObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Falsey object to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Falsey object to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Negative number {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNegativeNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Negative number to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Negative number to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNegativeNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Negative number to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Negative number to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNegativeNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Negative number to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Negative number to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="c1"&gt;// Negative Zero {{{&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNegativeZero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Negative Zero to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Default if evaluates a Negative Zero to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNegativeZero&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Negative Zero to true // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"isset() if evaluates a Negative Zero to false&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fooNegativeZero&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Negative Zero to true&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"empty() if evaluates a Negative Zero to false // Uh oh! &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// }}}&lt;/span&gt;

&lt;span class="cm"&gt;/* vim: set foldmethod=marker */&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what the above script evaluates to (on my machine).&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PHP Version: 7.3.11
"Uh oh!" added after every logically unexpected result.
Default if evaluates null to false
isset() if evaluates null to false // Uh oh!
empty() if evaluates null to true
Default if evaluates unset variable to false
isset() if evaluates unset variable to false
empty() if evaluates an unset variable to true
Default if evaluates false to false
isset() if evaluates false to true
empty() if evaluates false to true
Default if evaluates true to true
isset() if evaluates true to true
empty() if evaluates true to false
Default if evaluates a falsey string to false
isset() if evaluates a falsey string to true // Uh oh!
empty() if evaluates a falsey string to true
Default if evaluates a truthy string to true
isset() if evaluates a truthy string to true
empty() if evaluates a truthy string to false
Default if evaluates a falsey array to false
isset() if evaluates a falsey array to true // Uh oh!
empty() if evaluates a falsey array to true
Default if evaluates a Truthy array to true
isset() if evaluates a Truthy array to true
empty() if evaluates a Truthy array to false
Default if evaluates a Falsey number to false
isset() if evaluates a Falsey number to true // Uh oh!
empty() if evaluates a Falsey number to true
Default if evaluates a Truthy number to true
isset() if evaluates a Truthy number to true
empty() if evaluates a Truthy number to false
Default if evaluates a Truthy object to true
isset() if evaluates a Truthy object to true
empty() if evaluates a Truthy object to false
Default if evaluates a Falsey object to true // Uh oh!
isset() if evaluates a Falsey object to true // Uh oh!
empty() if evaluates a Falsey object to false // Uh oh!
Default if evaluates a Negative number to true
isset() if evaluates a Negative number to true
empty() if evaluates a Negative number to false
Default if evaluates a Negative Zero to false
isset() if evaluates a Negative Zero to true // Uh oh!
empty() if evaluates a Negative Zero to true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>php</category>
      <category>booleans</category>
      <category>coercion</category>
    </item>
    <item>
      <title>Actually Using the General Sibling Combinator</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Thu, 30 Jul 2020 23:43:30 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/actually-using-the-general-sibling-combinator-2p16</link>
      <guid>https://forem.com/bronzehedwick/actually-using-the-general-sibling-combinator-2p16</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/actually-using-the-general-sibling-combinator/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve vaguely known about CSS’s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator"&gt;general sibling combinator &lt;/a&gt;for a while, but I have never found a practical use case for it, until now. And let me tell you, the results are, get ready for it, underwhelming.&lt;/p&gt;

&lt;p&gt;First, a quick review of what the general sibling combinator is. Or, skip ahead to the next sectionif this is old hat for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the general sibling combinator? ¶
&lt;/h2&gt;

&lt;p&gt;The general sibling combinator—invoked with &lt;code&gt;~&lt;/code&gt; between two selectors—allows you to select every sibling element following the first selectorthat matches the second selector.&lt;/p&gt;

&lt;p&gt;The general sibling combinator differs from the more common &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator"&gt;adjacent sibling combinator &lt;/a&gt;—invoked with &lt;code&gt;+&lt;/code&gt; between two selectors—in that it selects every following element despite interceding elements, while the adjacent sibling combinator only matches the first sibling after the first selector that also matches the second.&lt;/p&gt;

&lt;p&gt;To better illustrate the general sibling combinator, this CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;/* styles */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will match the following elements in this markup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Eyebrow&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;My title&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;First paragraph&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- matches --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/my/image.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"My image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Second paragraph&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- matches --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;blockquote&amp;gt;&lt;/span&gt;Give me quotes, or give me death.&lt;span class="nt"&gt;&amp;lt;/blockquote&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Third paragraph&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- matches --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the same code using adjacent sibling combinator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;/* styles */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will only match &lt;code&gt;&amp;lt;p&amp;gt;First paragraph&amp;lt;/p&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the general sibling combinator ¶
&lt;/h2&gt;

&lt;p&gt;The behavior of this combinator always felt too greedy to be helpful, or too easily replaced by simply targeting a group of classes, and that’s still the case most of the time; this is a rare pull in the tool belt.&lt;/p&gt;

&lt;p&gt;However, I was doing work for the &lt;a href="https://astoria.digital"&gt;astoria.digital &lt;/a&gt;website, a hacker group I volunteer with, and found it useful when working with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout"&gt;CSS Grid &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kF4oFOy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/actually-using-the-general-sibling-combinator/images/design.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kF4oFOy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/actually-using-the-general-sibling-combinator/images/design.png" alt="Astoria Digital design" width="800" height="469"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Astoria Digital design, with me egregiously highlighting the grid lines in red.&lt;/p&gt;



&lt;p&gt;The design called for a border along the grid content columns. If I were using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex"&gt;Flex &lt;/a&gt;, I could nest each column inside a wrapper and add the border to that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- …header markup… --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- …footer markup… --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, I could set &lt;code&gt;display: flex&lt;/code&gt; on &lt;code&gt;.wrapper&lt;/code&gt;, and each &lt;code&gt;.column&lt;/code&gt; becomes a flex item with the proper border.&lt;/p&gt;

&lt;p&gt;Similarly with grid, only direct children of &lt;code&gt;.wrapper&lt;/code&gt; become grid items, aka items that can be laid out using grid. And since I wanted the header, content columns and footer to all align, they all needed to be grid items. My code changed to this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- …header markup… --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- …footer markup… --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now my grid worked, but I could no longer target &lt;code&gt;.column&lt;/code&gt; for the border, since it’s real hard to target an element that doesn’t exist(believe me, I’ve tried).&lt;/p&gt;

&lt;p&gt;I could simply target each class in question…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that works! The only problem is that there’s a border surrounding &lt;code&gt;.wrapper&lt;/code&gt;, and we don’t want the last column’s border to “double up”with it.&lt;/p&gt;

&lt;p&gt;I could have re-factored my styles so that each grid cell abutting an edge provides the border the single &lt;code&gt;.wrapper&lt;/code&gt; border does, but that requires more effort to maintain, and was looking like a lot of effort to do in the first place, given that the actual code is more complicated than the simplified examples I’ve contrived for this post.&lt;/p&gt;

&lt;p&gt;So what about adding classes? A classic technique, just slap a &lt;code&gt;.last&lt;/code&gt; class on every element you don’t want the final border, then zero it outwith &lt;code&gt;border-right: none&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And yes, that works, but I’ve never liked utility classes like that,since they need to be applied to each element they operate on, which makes code more cumbersome to maintain.&lt;/p&gt;

&lt;p&gt;Instead, I often like to use one of the pseudo-classes like &lt;code&gt;:last-child&lt;/code&gt; or &lt;code&gt;:last-of-type&lt;/code&gt;, or the &lt;a href="https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/"&gt;lobotomized owl selector &lt;/a&gt;, which uses our more popular friend, the adjacent sibling combinator.&lt;/p&gt;

&lt;p&gt;Yet the lack of nesting was really crimping my options. I could have made it work with the above methods, but what I really wanted to do was to cancel the border for the &lt;em&gt;last column&lt;/em&gt;, not some random selector.&lt;/p&gt;

&lt;p&gt;Yes, it is the moment you’ve all been waiting for. I used… &lt;em&gt;The General Sibling Combinator!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s about time this sucker showed up, because this post is almost over.&lt;/p&gt;

&lt;p&gt;Why does it work? Since each column starts with a heading, and is an otherwise unique element (&lt;code&gt;h2&lt;/code&gt;), we can target the final one with &lt;code&gt;:last-of-type&lt;/code&gt;. That just targets the title, however, so we add the general sibling combinator to target all the following items.&lt;/p&gt;

&lt;p&gt;Putting it all together, the code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.title&lt;/span&gt;&lt;span class="nd"&gt;:last-of-type&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.title&lt;/span&gt;&lt;span class="nd"&gt;:last-of-type&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And only targets this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- …header markup… --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- targets this --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- targets this --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- targets this --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- targets this --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- …footer markup… --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you have it. One long-winded explanation for a very specific, opinionated use. However, if you’ve read this far, first off, &lt;strong&gt;Cheese Frogs&lt;/strong&gt;; that will be our secret code to identify each other.&lt;/p&gt;

&lt;p&gt;Second, I do think it’s worth having this one in my tool box, as I have a feeling it’ll come more in handy as I delve deeper into grid.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>css</category>
    </item>
    <item>
      <title>Introducing PCG: Or, How I'm Spending the Pandemic</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Sun, 10 May 2020 00:11:46 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/introducing-pcg-or-how-i-m-spending-the-pandemic-183o</link>
      <guid>https://forem.com/bronzehedwick/introducing-pcg-or-how-i-m-spending-the-pandemic-183o</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/introducing-pcg/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F1qt_APQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/abstract-point-and-clicks_hue9a9ca91ef1bf8bf1fe26f9c4d6063fd_131813_900x400_fill_q75_box_smart1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F1qt_APQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/abstract-point-and-clicks_hue9a9ca91ef1bf8bf1fe26f9c4d6063fd_131813_900x400_fill_q75_box_smart1.jpg" alt="An abstract, computer-manipulated and hardly recognizable image of many point and click adventure game screenshots" width="880" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A lot has changed in the world since I last posted.&lt;/p&gt;

&lt;p&gt;I have been extremely lucky during this pandemic. I am still employed,I can work from home, and I have my wife to shelter with. I do not take these things for granted.&lt;/p&gt;

&lt;p&gt;And yet.&lt;/p&gt;

&lt;p&gt;While my work life has not changed as drastically, my personal life has. Most of the things I did outside work before the pandemic were in person. Can’t do that right now. So, it gave me some time to work on home-bound projects that I pushed back on the shelf.&lt;/p&gt;

&lt;p&gt;To that end, I’m very excited to introduce &lt;a href="https://github.com/bronzehedwick/pcg"&gt;PCG&lt;/a&gt;, or Point and Click Game engine, an adventure game creation utility for the open web.&lt;/p&gt;

&lt;p&gt;I did a &lt;a href="https://dev.to/bronzehedwick/my-talk-at-queensjs-3m4g-temp-slug-5410243"&gt;talk about it&lt;/a&gt;three years ago (ouch), so this project has certainly been a long time coming.&lt;/p&gt;

&lt;p&gt;PCG is very much in active development, but I think I’ve made encouraging progress, which I’ll explore in detail later.&lt;/p&gt;

&lt;p&gt;But first, what am I talking about?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an adventure game?
&lt;/h2&gt;

&lt;p&gt;If this is old hat to you, skip ahead to the next section.&lt;/p&gt;

&lt;p&gt;For those not familiar, a point and click adventure game is a style of narrative, story-based games where progress is made primarily through puzzle solving, rather than violence or reflexes, something I appreciate more and more as I age.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VdSsOmPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/tentacle.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Day of the Tentacle screenshot" src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdSsOmPM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/tentacle.jpg" width="500" height="281"&gt;&lt;/a&gt; &lt;/p&gt;

Day of the Tentacle, a classic comedic point and click adventure game.




&lt;p&gt;While their popularity peaked in the early 90s for mainstream gaming cultural, they have thrived in the indie space over the past decade or so.&lt;/p&gt;

&lt;p&gt;Mechanically, many games in the genre use a system of &lt;em&gt;verbs&lt;/em&gt; to interact with the world. You click a verb from a menu, for example“push”, and then the person or object in the game you want to apply it to, such as “crate”. Perhaps there would be a trap door below the crate,and a new area is unlocked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cKUGmOxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/indy.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Indiana Jones and the Fate of Atlantis screenshot" src="https://res.cloudinary.com/practicaldev/image/fetch/s--cKUGmOxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/indy.jpg" width="500" height="375"&gt;&lt;/a&gt; &lt;/p&gt;

Indiana Jones and the Fate of Atlantis is a game that used a list of verbs.




&lt;p&gt;Another method some games employ is to do away with the specific list of verbs, having pre-determined actions when interacting, or relying on the player manipulating “mini-game” style set pieces, such as a series of levers that must be switched in the right order.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v6UGm7GJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/myst.jpg" class="article-body-image-wrapper"&gt;&lt;img alt="Myst screenshot" src="https://res.cloudinary.com/practicaldev/image/fetch/s--v6UGm7GJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/myst.jpg" width="500" height="313"&gt;&lt;/a&gt;&lt;br&gt;
Myst did away with set verbs, and instead utilized bespoke mini-puzzles to progress.&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;Almost all have you collecting various esoteric items, having the player apply those items to people or objects in the game, or combining them with each other.&lt;/p&gt;

&lt;p&gt;A relatively simple system, from a game mechanics perspective, but one that hides a lot of depth, story-telling potential, and that particular player satisfaction from figuring out a puzzle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a web-based adventure engine?
&lt;/h2&gt;

&lt;p&gt;Most innovation in the web game space is around the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;element and &lt;a href="https://webassembly.org/"&gt;Web Assembly&lt;/a&gt;, which allows developers to “start from scratch” and create entirely custom rendering divorced from any of the preconceptions of the web.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--02dgZBqO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/angry-bots.png" class="article-body-image-wrapper"&gt;&lt;img alt="Angry bots screenshot" src="https://res.cloudinary.com/practicaldev/image/fetch/s--02dgZBqO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.chrisdeluca.me/article/introducing-pcg/images/angry-bots.png" width="600" height="400"&gt;&lt;/a&gt;&lt;br&gt;
AngryBots is an example of a web game rendered via web assembly and the canvas tag.&lt;br&gt;
 &lt;/p&gt;

&lt;p&gt;This works well for action games or games with pixel-pushing graphics.However, the goal here is always to emulate a native application, and since games written for the browser cannot by definition ever be native,the best they can be is a close approximation.&lt;/p&gt;

&lt;p&gt;While close might be good enough, this always felt like a missed opportunity to me. We spend all these resources trying to get the web to be more like native applications, but hardly any on what new and interesting experiences we can create that are unique to the web. As Marshal McLuhan wrote, an author I’m proud to say I got a few pages into, &lt;a href="https://en.wikipedia.org/wiki/The_medium_is_the_message"&gt;the medium is the message&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I started thinking about what kind of games would work well inside the traditional web context - aka, HTML, CSS and JavaScript (and SVG)rendered into a DOM tree.&lt;/p&gt;

&lt;p&gt;After some thought, I settled on point-and-click adventure games.&lt;/p&gt;

&lt;p&gt;My reasons being:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;They are not real-time games&lt;/strong&gt; —Having game play that relies on any kind of precise timing are going to need a more controllable rendering model than the traditional web.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They rely on text/audio&lt;/strong&gt; —Text is a first class citizen of the web, and new web audio APIs make that aspect possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They are narrative-driven&lt;/strong&gt; —The web is a powerful method of communication, and I’m excited by new methods of leveraging that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I like them 😄&lt;/strong&gt; —This is important, because without it I wouldn’t be able to finish a big project like this.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In short, I thought I could re-create many of the different point and click adventure paradigms on the web, while taking full advantage of the things that make the web &lt;em&gt;the web&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Some of the unique things that are attractive about the web are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It’s universal&lt;/strong&gt; —Many more people have access to a web browser than those with access to a machine that can play a triple A game.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It’s accessible by default (with a rich API for extensions)&lt;/strong&gt;—This enables access by those with visual, auditory, motor, or other disabilities. Accessibility is sadly an afterthought in a lot of digital design, and seems entirely absent in the gaming space. Treating accessibility as a first class citizen makes the experience &lt;a href="http://www.mediaaccess.org.au/digitalaccessibilityservices/why-accessible-design-is-better-design/"&gt;better for everyone&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It’s sharable&lt;/strong&gt; —An oft taken for granted killer feature of the web is URLs. The power of sharing a permanent link that will work in every browser and can be posted to any platform is one I cannot understate.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Design goals
&lt;/h2&gt;

&lt;p&gt;The ultimate goal of PCG is to foster a open, welcoming, and creative community around making point and click adventure games on the web.&lt;/p&gt;

&lt;p&gt;In game engine terms, the goal is to create a flexible, modular, and pluggable system of components that can be combined to create most if not all the point and click varieties mentioned above (and many that were not), as well as opening up the possibility for new and unique games only possible in the web format.&lt;/p&gt;

&lt;p&gt;After a lot more thought, writing, re-writing, trial and error, and leveraging embarrassingly earned career experience, I settled on some design principles for PCG.&lt;/p&gt;

&lt;p&gt;The thought of even having design principles was something hard earned,but one I strongly believe in: a north star for how you go about making something out of nothing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Leverage core web tech (HTML/CSS/JS)&lt;/strong&gt;—Rely on core web technologies and patterns over writing new systems. While new systems may offer benefits, building off existing ones usually means a more familiar, fast, and pleasant player experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Player experience over developer experience&lt;/strong&gt; —While developers are important, the end result that players consume takes precedence over the experience of the developers creating the game. These first two principles are why PCG is built without a framework in vanilla HTML/CSS/JS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Through documentation&lt;/strong&gt; —A well documented system is an understood system, and an understood system is a powerful tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Newbie friendly&lt;/strong&gt; —As the web has professionalized, many exciting capabilities have opened up. It has also raised the barrier to entry. Creating something fun and expressive that can be used at a basic level to good results, while still offering a much larger world of possibility for those interested in learning, I think strikes the right balance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; —This is essential to creating a community, which is critical to the success of a tiny project like this. I also believe in it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;This is a very high level introduction to the ideas surrounding the PCG project. I plan on writing posts going in-depth on each component of the system as they’re built and as updates are made. These posts will hopefully serve as a living progress report.&lt;/p&gt;

&lt;p&gt;While I’ve spent a lot of time on PCG already, it is still in the beginning stages. It is very much a leap of faith.&lt;/p&gt;

&lt;p&gt;I can’t predict what kind of community it will attract, if any, or what this project may or may not evolve into.&lt;/p&gt;

&lt;p&gt;But I am excited to find out.&lt;/p&gt;




&lt;p&gt;You can check out the &lt;a href="https://github.com/bronzehedwick/pcg"&gt;Github repository&lt;/a&gt; or the &lt;a href="https://pcg.readthedocs.io/en/latest/"&gt;documentation site&lt;/a&gt; for PCG, both very much in progress. If you have any feedback or would like to contribute, please don’t hesitate to reach out.&lt;/p&gt;

&lt;p&gt;Thanks for reading this far, hope you and yours are safe and healthy, and I’ll catch you on the next adventure.&lt;/p&gt;

</description>
      <category>games</category>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My Facebook Ad Data</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Sun, 17 Mar 2019 21:09:34 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/my-facebook-ad-data-4o3e</link>
      <guid>https://forem.com/bronzehedwick/my-facebook-ad-data-4o3e</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://www.chrisdeluca.me/article/facebook-ad-data/"&gt;Chris DeLuca's blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today I downloaded all the data Facebook has on me, and started poking through it. Since it’s been the focus of every privacy scandal, I went straight to the ad data. I found two items.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My ad “interests”, a simple keyword list&lt;/li&gt;
&lt;li&gt;A file called &lt;code&gt;advertisers_who_uploaded_a_contact_list_with_your_information.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, Facebook made more than &lt;a href="https://www.theguardian.com/technology/2018/jan/31/facebook-profit-mark-zuckerberg"&gt;4 billion dollars last year&lt;/a&gt;, and their business model is built entirely on &lt;a href="https://www.forbes.com/sites/lensherman/2018/04/16/why-facebook-will-never-change-its-business-model/#4dabf49864a7"&gt;selling user behavior to advertisers&lt;/a&gt;, so it seems impossible that not only is this all the data they have on me, but that it’s also this simplistic. Be that as it may, let’s take it at face value.&lt;/p&gt;

&lt;h2&gt;
  
  
  My “interests”
&lt;/h2&gt;

&lt;p&gt;There are 39 “topics” that Facebook has identified as one of my interests. I’ve been on Facebook since 2010, but was never a heavy user,and have cut back significantly in the past several years, so the data is pleasantly skewed.&lt;/p&gt;

&lt;p&gt;Here’s the whole list.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Alternative dance&lt;/li&gt;
&lt;li&gt;Alternative rock&lt;/li&gt;
&lt;li&gt;American Gods (TV series)&lt;/li&gt;
&lt;li&gt;Arts and music&lt;/li&gt;
&lt;li&gt;Batman Begins&lt;/li&gt;
&lt;li&gt;Blues rock&lt;/li&gt;
&lt;li&gt;British blues&lt;/li&gt;
&lt;li&gt;Casual game&lt;/li&gt;
&lt;li&gt;Chillwave&lt;/li&gt;
&lt;li&gt;Cillian Murphy&lt;/li&gt;
&lt;li&gt;Cream (band)&lt;/li&gt;
&lt;li&gt;Deathwish Inc.&lt;/li&gt;
&lt;li&gt;Decorative arts&lt;/li&gt;
&lt;li&gt;Dunce&lt;/li&gt;
&lt;li&gt;Dune (novel)&lt;/li&gt;
&lt;li&gt;Dungeons &amp;amp; Dragons&lt;/li&gt;
&lt;li&gt;Emo (music)&lt;/li&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Folk art&lt;/li&gt;
&lt;li&gt;Folk rock&lt;/li&gt;
&lt;li&gt;Frank Herbert&lt;/li&gt;
&lt;li&gt;Future (rapper)&lt;/li&gt;
&lt;li&gt;Games&lt;/li&gt;
&lt;li&gt;Glidden (paints)&lt;/li&gt;
&lt;li&gt;Grunge&lt;/li&gt;
&lt;li&gt;Hardcore punk&lt;/li&gt;
&lt;li&gt;Hong Kong&lt;/li&gt;
&lt;li&gt;Humans of New York&lt;/li&gt;
&lt;li&gt;Indie rock&lt;/li&gt;
&lt;li&gt;Ken Watanabe&lt;/li&gt;
&lt;li&gt;Led Zeppelin&lt;/li&gt;
&lt;li&gt;Mannequin&lt;/li&gt;
&lt;li&gt;Paper&lt;/li&gt;
&lt;li&gt;Pop music&lt;/li&gt;
&lt;li&gt;Professional wrestling match types&lt;/li&gt;
&lt;li&gt;Rock music&lt;/li&gt;
&lt;li&gt;Studio&lt;/li&gt;
&lt;li&gt;Tom Wilkinson&lt;/li&gt;
&lt;li&gt;Wally Pfister&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without giving too much away (because why give advertisers a handout?),I’d say I have a genuine interest in about half the listed topics, which is honestly a pretty good (for them) error rate for the kind of dragnet campaigns advertisers run on the web.&lt;/p&gt;

&lt;p&gt;Yet some of these keywords are bonkers. Like “Studio”. Studio? What ad exec in their right mind wakes up and says, “You know who we need to target? People with a keen interest in ‘Studio’.”&lt;/p&gt;

&lt;p&gt;Here be the glories of the algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uploaded contact list
&lt;/h2&gt;

&lt;p&gt;There are 1,349 “advertisers who uploaded a contact list with your information.” There’s no indication on how many times this data was uploaded, or where it was uploaded, or even what data was uploaded. It’s just a list of companies.&lt;/p&gt;

&lt;p&gt;Some of the companies I recognize, like Airbnb and AARP (zing?), and others I had to look up, like Ebates and ConversioBot (A shady cash loan service and a shady chat bot service, respectively).&lt;/p&gt;

&lt;p&gt;And some companies seemed to want to &lt;em&gt;really&lt;/em&gt; cover their bases, like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Toyota Escondido&lt;/li&gt;
&lt;li&gt;Toyota Hawaii&lt;/li&gt;
&lt;li&gt;Toyota Marin&lt;/li&gt;
&lt;li&gt;Toyota of Bedford&lt;/li&gt;
&lt;li&gt;Toyota Of Berkeley&lt;/li&gt;
&lt;li&gt;Toyota of Braintree&lt;/li&gt;
&lt;li&gt;Toyota of Cedar Park&lt;/li&gt;
&lt;li&gt;Toyota of Dallas&lt;/li&gt;
&lt;li&gt;Toyota of Des Moines&lt;/li&gt;
&lt;li&gt;Toyota of Dothan&lt;/li&gt;
&lt;li&gt;Toyota of Easley&lt;/li&gt;
&lt;li&gt;Toyota of Fayetteville&lt;/li&gt;
&lt;li&gt;Toyota of Fort Worth&lt;/li&gt;
&lt;li&gt;Toyota of Hackensack&lt;/li&gt;
&lt;li&gt;Toyota of Hollywood&lt;/li&gt;
&lt;li&gt;Toyota of Irving&lt;/li&gt;
&lt;li&gt;Toyota of Massapequa&lt;/li&gt;
&lt;li&gt;Toyota of Melbourne&lt;/li&gt;
&lt;li&gt;Toyota of Nashua&lt;/li&gt;
&lt;li&gt;Toyota of Pharr&lt;/li&gt;
&lt;li&gt;Toyota of Redlands&lt;/li&gt;
&lt;li&gt;Toyota of Scranton&lt;/li&gt;
&lt;li&gt;Toyota of Southern Maryland&lt;/li&gt;
&lt;li&gt;Toyota of Stamford&lt;/li&gt;
&lt;li&gt;Toyota of Surprise&lt;/li&gt;
&lt;li&gt;Toyota of Tampa Bay&lt;/li&gt;
&lt;li&gt;Toyota of Terre Haute&lt;/li&gt;
&lt;li&gt;Toyota of Turnersville&lt;/li&gt;
&lt;li&gt;Toyota of Wallingford&lt;/li&gt;
&lt;li&gt;Toyota of York&lt;/li&gt;
&lt;li&gt;Toyota Santa Monica&lt;/li&gt;
&lt;li&gt;Toyota South&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I sifted through the companies looking for anything overtly political, since that’s the &lt;a href="https://theintercept.com/2018/03/14/facebook-election-meddling/"&gt;&lt;em&gt;really&lt;/em&gt; big focus of Facebook’s scandals&lt;/a&gt;. All I found was a single, lonely company:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conservative Party of New York State&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’m not entirely sure how I feel about it, but I can pretty safely say they’re barking up the wrong tree.&lt;/p&gt;

&lt;p&gt;But remember, this data isn’t just mine (as much as any of this data is ours). It’s yours, too, because these data talks about where I showed up in a contact list, which includes however many other people.&lt;/p&gt;

&lt;p&gt;Scary? Don’t worry, things feel so much better from behind the wheel of a brand new Toyota!&lt;/p&gt;

</description>
      <category>facebook</category>
      <category>privacy</category>
      <category>data</category>
    </item>
    <item>
      <title>Don't commit that file!</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Thu, 29 Nov 2018 01:05:36 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/dont-commit-that-file-n80</link>
      <guid>https://forem.com/bronzehedwick/dont-commit-that-file-n80</guid>
      <description>&lt;p&gt;I wrote a small git &lt;code&gt;pre-commit&lt;/code&gt; hook to prevent committing certain files. There are more words to this, but if you're impatient, you can skip right to the goods.&lt;/p&gt;

&lt;p&gt;At work, we have some configuration files tracked in git that we modify locally to enable debugging options. We don't want to ignore these files and have to manage them in a different system outside of git, but we also don't want the debugging options checked in.&lt;/p&gt;

&lt;p&gt;So we keep the files tracked in git, and modify them on our local systems, and try to remember not to check in those debugging options.&lt;/p&gt;

&lt;p&gt;After the debugging changes ended up in a pull request of mine, I had an idea: since I'm a computer programmer, what if I could &lt;em&gt;use&lt;/em&gt; my computer to save myself from myself? It was just crazy enough to work.&lt;/p&gt;

&lt;p&gt;What I really wanted was for git to prevent me from committing changes to these files, physically if necessary. The answer: &lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks" rel="noopener noreferrer"&gt;git hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Git hooks are custom scripts that run inside your local repository when one of several actions is taken, like committing, and merging, and the like. They're very powerful, since they can be any script that runs in a shell, but like most things in computer science, they still can't throw a punch. That meant my script would need to throw an error instead to keep me from committing those debugging changes.&lt;/p&gt;

&lt;p&gt;A few minutes later I had cobbled together a git &lt;code&gt;pre-commit&lt;/code&gt; hook script that prevents any of the unwanted files from being changed. The pre commit hook runs, as the name heavily implies, &lt;em&gt;before&lt;/em&gt; the commit happens, so if one of the no-no files is in the changeset, I get a nice big error when I run &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's what I came up with:&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# This script prevents specific file modifications from taking place.&lt;/span&gt;
&lt;span class="c"&gt;# We want certain config files checked into git so that builds work on a clone,&lt;/span&gt;
&lt;span class="c"&gt;# *and* we need to modify these files locally to enable debug options.&lt;/span&gt;
&lt;span class="c"&gt;# This leads to a scenario where we can accidentally check in the config files&lt;/span&gt;
&lt;span class="c"&gt;# with our local debug options checked in. This script prevents that.&lt;/span&gt;

&lt;span class="c"&gt;# Get current revision to check against.&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--verify&lt;/span&gt; HEAD &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;against&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;HEAD
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="c"&gt;# Initial commit: diff against an empty tree object&lt;/span&gt;
  &lt;span class="nv"&gt;against&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git hash-object &lt;span class="nt"&gt;-t&lt;/span&gt; tree /dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Redirect output to stderr.&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;1&amp;gt;&amp;amp;2

&lt;span class="c"&gt;# Test staged files against the files we don't want to check in,&lt;/span&gt;
&lt;span class="c"&gt;# and abort if found.&lt;/span&gt;
git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$against&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; file&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;do
  if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"path/to/my/unchanagble/file.yml"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Don't check in file.yml. Aborting!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi

  if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"some/other/file.php"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Don't check in file.php. Aborting!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Repeat pattern as necessary.&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic sauce is near the end; I loop over the output of &lt;code&gt;git diff --cached --name-only&lt;/code&gt;, which shows the name of each staged file, and check if the file name matches one of the files I don't want to commit. If the file matches, &lt;code&gt;exit&lt;/code&gt; with a non-zero status, and git will happily prevent me from making that commit. Hooray!&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Machine Learning</title>
      <dc:creator>Chris DeLuca</dc:creator>
      <pubDate>Tue, 28 Aug 2018 20:13:25 +0000</pubDate>
      <link>https://forem.com/bronzehedwick/machine-learning-1ene</link>
      <guid>https://forem.com/bronzehedwick/machine-learning-1ene</guid>
      <description>&lt;p&gt;I took a week off from work to join a deep dive study group on machine learning.&lt;br&gt;
It was an incredible experience and I want to tell you about what I learned.&lt;/p&gt;

&lt;p&gt;I learned with very smart and interesting people, and I encourage you to check out their work. You can see a list of everyone at the bottom of this article.&lt;/p&gt;

&lt;p&gt;This is a birds-eye-view article, so hopefully it will be decipherable by laypersons, while still remaining valuable to anyone who doesn’t know what this field is about.&lt;/p&gt;

&lt;p&gt;How should we delve into this incredibly complex topic? How about using the journalistic method of asking the classic five "W" questions: who, what, when, where, and why.&lt;/p&gt;

&lt;p&gt;That seems like a successfully pointless structure, so let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is machine learning
&lt;/h2&gt;

&lt;p&gt;Machine learning is a broad term referring to training a computer using one data set to make predictions about a different dataset, without further human input.&lt;/p&gt;

&lt;p&gt;For example, say you had a breakdown of sales for the past decade from a particular Orange Julius. Using machine learning, you might be able to have the machine predict when the most popular sales days will be for the mango pineapple smoothie in the upcoming year.&lt;/p&gt;

&lt;p&gt;Why do I say &lt;em&gt;might&lt;/em&gt;? In short, because it all comes down to the quality, and quantity, of your data.&lt;/p&gt;

&lt;p&gt;In the above example, maybe the weather, or the stock market, or the cycle of the moon influence whether people want a mango pineapple smoothie or a raspberry one.&lt;/p&gt;

&lt;p&gt;You need to have as much relevant data as you can to make the system work. If there's a correlation between people wanting strawberry banana smoothies and the groundhog mating season, and you don't have that data, guess what? You can't make that connection.&lt;/p&gt;

&lt;p&gt;However, if there is a connection between two data points---aka, groundhog mating and strawberry banana smoothies---you, the human, need to tell the machine about it to make the system go.&lt;/p&gt;

&lt;p&gt;At the end of the day, computers are dumb. They literally don't know their ass from their elbow. Contrary to every movie featuring robots, computers are self aware much the same way a brick is.&lt;/p&gt;

&lt;p&gt;A computer is so dumb, it can't figure out the relationship between data in both directions. This means that if you &lt;em&gt;think&lt;/em&gt; there's a relationship between Guatemalan migratory bird patterns and an uptick in blueberry smoothie sales, &lt;em&gt;and you're wrong&lt;/em&gt;, the computer won't know! If you plug in that relationship, the machine will happily spit out a number. Never mind that it's meaningless, the computer did its job.&lt;/p&gt;

&lt;p&gt;So it's our job, as the humans, to really make sure our thought quality is good before ascribing meaning to things. That's probably a good call in general.&lt;/p&gt;

&lt;p&gt;Oddly enough, in all these Orange Julius examples, nobody wants orange.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is machine learning
&lt;/h2&gt;

&lt;p&gt;Machine learning exists for the same reason we do anything: first, curiosity, then art, and then finally money. Currently, all three reasons exist in perfect &lt;del&gt;harmony&lt;/del&gt; tension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where is machine learning
&lt;/h2&gt;

&lt;p&gt;All over the place. Here are just a few examples.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://deepdreamgenerator.com/#gallery"&gt;Image generation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://searchengineland.com/google-uses-machine-learning-search-algorithms-261158"&gt;Google searches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://data.fivethirtyeight.com/"&gt;FiveThirtyEight political prediction models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://machinelearning.apple.com/2017/11/16/face-detection.html"&gt;Apple's Face ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.fastcompany.com/3028414/how-facebooks-machines-got-so-good-at-recognizing-your-face"&gt;Facebook's facial recognition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=qv6UVOQ0F44"&gt;Playing Mario really well&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these applications use algorithms like neural networks, deep learning, and others that we didn't get into in this article. While the specifics (read: math) differ greatly, the basic principles still apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is machine learning
&lt;/h2&gt;

&lt;p&gt;Now, and into the foreseeable future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is machine learning
&lt;/h2&gt;

&lt;p&gt;In short: not many people. It’s a specialty inside (at least one other) specialty. Practitioners need an understanding of programming, statistics, calculus, plus the vagaries of whatever aspect of life they are trying to make predictions on. That’s on top of machine learning specifics.&lt;/p&gt;

&lt;p&gt;That’s one of the reasons I wanted to study it. The more people know about machine learning the more we as a society will be able to deal with its consequences.&lt;/p&gt;

&lt;p&gt;At least that’s the prediction; I haven’t proved that in a model yet 🤓.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  With whom I learned
&lt;/h3&gt;

&lt;p&gt;I'd like to give a big shout out to the super smart and interesting folks I worked with over the week. Here they are, along with links to what they're up to. I highly encourage you to check them out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://matthewwillse.com"&gt;Matthew Willse---Product Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mani.io"&gt;Mani Nilchiani---Artist, Programmer, Musician, Imposter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.campbell-watson.com/"&gt;Campbell Watson---Atmospheric Scientist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://leetusman.com"&gt;Lee Tusman---artist/coder/educator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The post &lt;a href="https://www.chrisdeluca.me/article/machine-learning/"&gt;Machine Learning&lt;/a&gt; first appeared on &lt;a href="https://www.chrisdeluca.me/"&gt;Chris DeLuca&lt;/a&gt;'s blog.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
