<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tools on Clément Joly – Open-Source, Rust &amp; SQLite</title><link>https://joly.pw/tags/tools/</link><description>Recent content in Tools on Clément Joly – Open-Source, Rust &amp; SQLite</description><image><title>Clément Joly – Open-Source, Rust &amp; SQLite</title><url>https://joly.pw/images/open-graph-home-original.png</url><link>https://joly.pw/images/open-graph-home-original.png</link></image><generator>Hugo -- 0.154.3</generator><language>en</language><copyright>Clément Joly</copyright><atom:link href="https://joly.pw/tags/tools/index.xml" rel="self" type="application/rss+xml"/><item><title>Hmrc2ledger</title><link>https://joly.pw/hmrc2ledger/</link><pubDate>Sun, 21 Aug 2022 08:15:54 +0100</pubDate><guid>https://joly.pw/hmrc2ledger/</guid><description>Put HMRC rates in a ledger database</description><content:encoded><![CDATA[<p>
<p style="display: flex; justify-content: space-between">
  <a href="https://github.com/cljoly/hmrc2ledger" data-goatcounter-click="ext-github-hmrc2ledger" data-goatcounter-title="cljoly/hmrc2ledger">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
    stroke-linecap="round" stroke-linejoin="round">
    <path
        d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22">
    </path>
</svg></span>&nbsp;cljoly/hmrc2ledger
  </a>
  <a class="badges" href="https://github.com/cljoly/hmrc2ledger" data-goatcounter-click="ext-stargithub-hmrc2ledger" data-goatcounter-title="stars cljoly/hmrc2ledger">
    <img src="https://img.shields.io/github/stars/cljoly/hmrc2ledger?style=social" alt="Github stars for hmrc2ledger">
  </a>
</p>


<div class="badges">

</p>
<p><a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a></p>

</div>


<p><strong><em>Disclaimer</em>: This is not financial advice. It may be outdated or wrong, do your own research.</strong></p>
<p>Convert <a href="https://www.gov.uk/government/collections/exchange-rates-for-customs-and-vat">CSV</a> to <a href="https://www.ledger-cli.org/">ledger</a> price db, to compute <a href="https://www.gov.uk/hmrc-internal-manuals/capital-gains-manual/cg78310">foreign</a> transaction CGT (and <a href="https://www.gov.uk/hmrc-internal-manuals/capital-gains-manual/cg78316">section 104</a> holdings).</p>




  <figure>
    <blockquote >
      <p>Contains public sector information licensed under the <a href="https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/">Open Government Licence v3.0</a></p>

    </blockquote>
    
  </figure>



]]></content:encoded></item><item><title>Ledger.ctags</title><link>https://joly.pw/ledger-ctags/</link><pubDate>Sun, 21 Aug 2022 08:15:54 +0100</pubDate><guid>https://joly.pw/ledger-ctags/</guid><description>Ctags file for ledger-cli</description><content:encoded><![CDATA[
<p style="display: flex; justify-content: space-between">
  <a href="https://github.com/cljoly/ledger.ctags" data-goatcounter-click="ext-github-ledger.ctags" data-goatcounter-title="cljoly/ledger.ctags">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
    stroke-linecap="round" stroke-linejoin="round">
    <path
        d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22">
    </path>
</svg></span>&nbsp;cljoly/ledger.ctags
  </a>
  <a class="badges" href="https://github.com/cljoly/ledger-ctags" data-goatcounter-click="ext-stargithub-ledger.ctags" data-goatcounter-title="stars cljoly/ledger.ctags">
    <img src="https://img.shields.io/github/stars/cljoly/ledger.ctags?style=social" alt="Github stars for ledger.ctags">
  </a>
</p>

<p>If you are using <a href="https://ledger-cli.org/">Ledger</a> or one of its variants for your <a href="https://plaintextaccounting.org/">plain text accounting</a>, you get to manage your finances a bit more like you manage code. With plain text files, in your favorite editor.</p>
<p>And yet, as opposed to code in most languages, you don’t have a <a href="https://microsoft.github.io/language-server-protocol/">Language Server Protocol implementation</a>. So it’s a bit harder to get completions on accounts, tags, payees… You have to rely on bespoken support from your editor and that’s often imperfect or slow. Plus, if you <a href="https://www.ledger-cli.org/3.0/doc/ledger3.html#index-pre_002ddeclare-account">declare</a> your accounts and payees, you also can’t jump to the definition easily.</p>
<p>Enter ctags configuration for ledger, which this project provides. It generates <code>tag</code> file that’s understood by your editor, often out of the box (<a href="https://vimhelp.org/usr_29.txt.html#29.1">vim/neovim</a>, <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Tags-Tables.html">emacs</a>) or with a generic plugin (Atom, VS Code, TextMate and <a href="https://en.wikipedia.org/wiki/Ctags#Editors_that_support_ctags">more</a>). Congrats, your editor now has very fast completions and code navigation!</p>
<h2 id="installation">Installation</h2>
<p>You need to have <a href="https://ctags.io/">ctags</a> installed<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Then drop <a href="https://raw.githubusercontent.com/cljoly/ledger.ctags/main/ledger.ctags"><code>ledger.ctags</code></a> in <code>~/.config/ctags</code> (or whatever <code>ctags</code> is configured to read).</p>
<h2 id="usage">Usage</h2>
<pre tabindex="0"><code>ctags my/ledger/journal.ldg
</code></pre><p>will collect all the elements formally <a href="https://www.ledger-cli.org/3.0/doc/ledger3.html#index-pre_002ddeclare-account">declared</a> in the journal file. This means that in the following file:</p>
<pre tabindex="0"><code class="language-ledger" data-lang="ledger">tag UUID
account Assets
payee Shop
commodity EUR

2022-01-01 * Someone
    Expenses:Food   $10
    Liabilities:Credit Card
</code></pre><p>only <code>UUID</code>, <code>Assets</code>, <code>Shop</code> and <code>EUR</code> are detected, because they are the only one declared and so the only ones with a definition to jump to. <code>Someone</code>, <code>Expenses</code>, <code>Expenses:Food</code>, <code>$</code>, <code>Liabilities</code> and <code>Liabilities:Credit  Card</code> are not declared, so they are ignored by ctags.</p>
<h3 id="initial-list-of-accounts-payees">Initial List of Accounts, Payees…</h3>
<p>As mentioned, ctags only collects elements that are declared. You could write the declarations manually, if you want to have fine-grained control of which accounts you want to actively use and have completions for. But to ease initial bootstrapping, you may want to start by adding everything you currently use in your ledger journals. You can do that with a couple commands.</p>
<p>For accounts, you can use this bash snippet:</p>
<pre tabindex="0"><code>ledger accounts | awk &#39;{ print &#34;account&#34;, $0}&#39;
</code></pre><p>For payees:</p>
<pre tabindex="0"><code>ledger payees | awk &#39;{ print &#34;payee&#34;, $0}&#39;
</code></pre><p>For tags:</p>
<pre tabindex="0"><code>ledger tags | awk &#39;{ print &#34;tag&#34;, $0}&#39;
</code></pre><p>The snippets above output proper ledger definitions for accounts, payees and tags. You can insert these outputs in your journal or in a separate file with all your declarations (say <code>declarations.ledger</code>), that you can then <code>include</code> in your main journal file.</p>
<p>Don’t forget that ctags does not follow includes, so you should pass the file with the definitions to ctags (e.g. <code>ctags declarations.ledger</code>)</p>
<h3 id="multiple-files">Multiple files</h3>
<p>If you have multiple ledger journal files, pass them all to ctags (e.g. <code>ctags *.ledger *.ldg</code>) or run it on the current directory:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>ctags -R .
</span></span></code></pre></div><hr>
<h2 id="automatic-update-with-chezmoi">Automatic Update with Chezmoi</h2>
<p>Chezmoi <a href="https://www.chezmoi.io/reference/special-files-and-directories/chezmoiexternal-format/">external</a> feature allows you to auto update and manage the <code>ledger.ctags</code> file, by adding the following to your <code>~/.local/share/chezmoi/.chezmoiexternal.toml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#98c379">&#34;.config/ctags/&#34;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">type</span> = <span style="color:#98c379">&#34;archive&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">include</span> = [ <span style="color:#98c379">&#34;*/ledger.ctags&#34;</span> ]
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">url</span> = <span style="color:#98c379">&#34;https://github.com/cljoly/ledger.ctags/archive/master.zip/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">stripComponents</span> = <span style="color:#d19a66">1</span>
</span></span></code></pre></div><h2 id="contributing">Contributing</h2>
<p>Contributions (documentation or code improvements in particular) are welcome, see <a href="https://cj.rs/docs/contribute/">contributing</a>!</p>
<p>Feel free <a href="https://github.com/cljoly/ledger.ctags/issues/new">to fill an issue</a> if your <em>declared</em> accounts, tags, payee, alias… are not properly detected.</p>
<h2 id="acknowledgments">Acknowledgments</h2>
<p>I would like to thank the creators, and all the contributors, of <a href="https://ctags.io/">ctags</a> and <a href="https://ledger-cli.org/">ledger</a>. This is small configuration to make these two great projects work better together.</p>

<div class="badges">


<p><a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a></p>

</div>


<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Another implementation may work, but it’s untested. Feel free to send a PR though!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Generating Snippets with LuaSnip in Neovim</title><link>https://joly.pw/blog/generating-snippets-with-luasnip/</link><pubDate>Sun, 31 Jul 2022 22:10:49 +0100</pubDate><guid>https://joly.pw/blog/generating-snippets-with-luasnip/</guid><description>Generating repetitive snippets with code, like time variant of section titles.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>When you have many variations of the same snippet, one option is to generate those with Lua code. The complete example is <a href="#the-final-code">at the end</a>.</p>
  </div>



<p>I’ve <a href="https://joly.pw/blog/ultisnips-to-luasnip/">recently moved</a> to <a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> as my snippets plugin for Neovim.
When I first started, I sticked to the simplest features of LuaSnip, in particular the SnipMate-like syntax for snippets.
But I have now started to explore the more distinctive features of LuaSnip, like Lua-defined snippets.
It turns out that generating snippets with code can save tedious repetition.</p>
<h2 id="markdown-journaling">Markdown Journaling</h2>
<p>I tend to take notes in Markdown documents.
I usually set up one text file for each recurring themes or project.
Then, there is a “h1” title per day, a bit like the <a href="https://help.bulletjournal.com/article/27-daily-log">Bullet Journal daily log</a>.
The resulting file looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-markdown" data-lang="markdown"><span style="display:flex; background-color:#3d4148"><span># 2022-07-31 Sun
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">*</span> Point 1
</span></span><span style="display:flex;"><span><span style="color:#c678dd">*</span> Point 2
</span></span><span style="display:flex;"><span><span style="color:#c678dd">*</span> Point 3
</span></span></code></pre></div><p>I have a snippet to generate the <code># 2022-07-31 Sun</code> line, based on the current date and day of the week, as I explained <a href="https://joly.pw/blog/ultisnips-to-luasnip/#advanced-snippets-with-environment-variables">last time</a>.
For reference, here is the snippet from back then:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet"># “Bullet Journal”-styled date for today
snippet bjtoday
  # ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} $CURRENT_DAY_NAME_SHORT
</code></pre><h2 id="lua-generated-snippets">Lua-Generated Snippets</h2>
<p>But then, I also sometimes want to put notes for the next session.
I usually know when that will be, because the meeting happens, say, every Thursday or every month.
So it would be great to have snippets for those dates as well.
Let’s build them step by step.</p>
<h3 id="1-computing-the-date-of-next-friday">1. Computing the Date of “Next Friday”</h3>
<p>The first step is to calculate the date and weekday from human concepts like “next Friday” or “next week”.
Thankfully <a href="https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html#date-invocation">GNU date</a> supports this, and it’s preinstalled on most GNU/Linux systems:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ date -d <span style="color:#98c379">&#39;next Friday&#39;</span> +<span style="color:#98c379">&#39;%F %a&#39;</span>
</span></span><span style="display:flex;"><span>2022-08-05 Fri
</span></span></code></pre></div><h3 id="2-putting-those-dates-in-luasnip">2. Putting Those Dates in LuaSnip</h3>
<p>The second step is to use the output of the <code>date</code> command in LuaSnip.</p>
<p>We could <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#variables">define our own variables</a> like <code>${NEXT_MONDAY_DATE}</code>.
That would be similar to the <code>${CURRENT_DATE}</code> we used before.
But we would need many such variables, for the date and weekday of the next Monday, Tuesday, …, next week or next month.
Plus I use those dates only in Markdown snippets, so it feels a bit wasteful to define those variables everywhere.</p>
<p>In the end, I chose to use <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#functionnode">function nodes</a> to call the <code>date</code> command on the fly, when the snippet gets expanded.
This node is inserted in a particular snippet definition and is thus contained to that particular snippet.
This way, we avoid creating values everywhere, and we keep the global namespace clean.
It looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">f</span>(<span style="color:#c678dd">function</span>(<span style="color:#e06c75">args</span>, <span style="color:#e06c75">snip</span>, <span style="color:#e06c75">user_arg_1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">trim</span>(<span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">system</span>(<span style="color:#98c379">[[date -d &#39;]]</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">target_date</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">[[&#39; +&#39;%F %a&#39;]]</span>))
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>, {}),
</span></span></code></pre></div><h3 id="3-generating-snippets-variants">3. Generating Snippets Variants</h3>
<p>The last step is to generate the variants of the base snippet: <code>bj_today</code>, <code>bj_next_monday</code>, …, <code>bj_next_week</code> and <code>bj_next_month</code>.</p>
<p>In LuaSnip, snippets defined using Lua are just a table calling some agreed-upon functions.
Here is an example from the <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#basics">documentation</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">ls.add_snippets</span>(<span style="color:#98c379">&#34;all&#34;</span>, {
</span></span><span style="display:flex;"><span>	<span style="color:#e06c75">s</span>(<span style="color:#98c379">&#34;ternary&#34;</span>, {
</span></span><span style="display:flex;"><span>		<span style="color:#7f848e">-- equivalent to &#34;${1:cond} ? ${2:then} : ${3:else}&#34;</span>
</span></span><span style="display:flex;"><span>		<span style="color:#e06c75">i</span>(<span style="color:#d19a66">1</span>, <span style="color:#98c379">&#34;cond&#34;</span>), <span style="color:#e06c75">t</span>(<span style="color:#98c379">&#34; ? &#34;</span>), <span style="color:#e06c75">i</span>(<span style="color:#d19a66">2</span>, <span style="color:#98c379">&#34;then&#34;</span>), <span style="color:#e06c75">t</span>(<span style="color:#98c379">&#34; : &#34;</span>), <span style="color:#e06c75">i</span>(<span style="color:#d19a66">3</span>, <span style="color:#98c379">&#34;else&#34;</span>)
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>This creates a snippet (the <code>s</code> function) for <code>all</code> file types. This snippet will expand <code>ternary</code> into <code>cond ? then : else</code>, prompting the user to change the <code>cond</code>, <code>then</code> and <code>else</code> bits (function <code>i</code>) while <code>?</code> and <code>:</code> are “static text” (function <code>t</code>).</p>
<p>We want to write a function that builds a table of date snippet definitions.
That will be returned when the Lua module is loaded.</p>
<h4 id="the-final-code">The Final Code</h4>
<p>The final snippet looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 1</span><span><span style="color:#7f848e">-- Bullet Journal styled dates in the future</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 2</span><span><span style="color:#c678dd">local</span> <span style="color:#c678dd">function</span> <span style="color:#61afef;font-weight:bold">gen_date_snippets</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 3</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">snippets</span> <span style="color:#56b6c2">=</span> {}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 4</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">target_dates</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 5</span><span>    <span style="color:#98c379">&#34;today&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 6</span><span>    <span style="color:#98c379">&#34;tomorrow&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 7</span><span>    <span style="color:#98c379">&#34;next monday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 8</span><span>    <span style="color:#98c379">&#34;next tuesday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 9</span><span>    <span style="color:#98c379">&#34;next wednesday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">10</span><span>    <span style="color:#98c379">&#34;next thursday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">11</span><span>    <span style="color:#98c379">&#34;next friday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">12</span><span>    <span style="color:#98c379">&#34;next week&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">13</span><span>    <span style="color:#98c379">&#34;next month&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">14</span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">15</span><span>  <span style="color:#c678dd">for</span> <span style="color:#e06c75">_</span>, <span style="color:#e06c75">target_date</span> <span style="color:#c678dd">in</span> <span style="color:#e06c75">pairs</span>(<span style="color:#e06c75">target_dates</span>) <span style="color:#c678dd">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">16</span><span>    <span style="color:#e06c75">table.insert</span>(
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">17</span><span>      <span style="color:#e06c75">snippets</span>,
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">18</span><span>      <span style="color:#e06c75">s</span>(<span style="color:#98c379">&#34;bj_&#34;</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">target_date</span>:<span style="color:#e06c75">gsub</span>(<span style="color:#98c379">&#34; &#34;</span>, <span style="color:#98c379">&#34;_&#34;</span>), {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">19</span><span>        <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;# &#34;</span>,
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">20</span><span>        <span style="color:#e06c75">f</span>(<span style="color:#c678dd">function</span>(<span style="color:#e06c75">args</span>, <span style="color:#e06c75">snip</span>, <span style="color:#e06c75">user_arg_1</span>)
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">21</span><span>          <span style="color:#c678dd">return</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">trim</span>(<span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">system</span>(<span style="color:#98c379">[[date -d &#39;]]</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">target_date</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">[[&#39; +&#39;%F %a&#39;]]</span>))
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">22</span><span>        <span style="color:#c678dd">end</span>, {}),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">23</span><span>      })
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">24</span><span>    )
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">25</span><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">26</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">27</span><span>  <span style="color:#c678dd">return</span> <span style="color:#e06c75">snippets</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">28</span><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">29</span><span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">30</span><span><span style="color:#c678dd">return</span> <span style="color:#e06c75">gen_date_snippets</span>()
</span></span></code></pre></div><p>This generates the snippets name (line 18), for instance <code>bj_today</code> or <code>bj_next_tuesday</code> from the arguments passed to the <code>date</code> command.
The body of the snippet is made of text (<code>t &quot;# &quot;</code>) and our function node from earlier (line 20-22).
All those snippets get collected in a table that is returned at the end of the Lua module (line 30).
It’s then usable by LuaSnip.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generating a bunch of similar snippets like this turned out to be an unanticipated benefit of using Lua to define snippets.
Before using LuaSnip, I only had <code>bj_today</code>, <code>bj_tomorrow</code> and <code>bj_next_monday</code>.
If I had wanted to expand this set further, I would have to do some more copying and pasting, which quickly becomes unmaintainable.</p>
<p>Of course for simpler or less repetitive snippets, it’s probably best to use the less verbose SnipMate-like syntax presented in the <a href="https://joly.pw/blog/ultisnips-to-luasnip/">previous post</a>.</p>
]]></content:encoded></item><item><title>From UltiSnips to LuaSnip</title><link>https://joly.pw/blog/ultisnips-to-luasnip/</link><pubDate>Sun, 15 May 2022 07:06:49 +0100</pubDate><guid>https://joly.pw/blog/ultisnips-to-luasnip/</guid><description>Shaving off a few milliseconds from Neovim startup time.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p><a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> is fast and doesn’t have to be complicated. Give it a try!</p>
  </div>







  
  
  
    
    
  
  

  <details class="alert alert-note">
    <summary class="alert-heading">
      ℹ️
      
        About UltiSnips
      
    </summary>
    <p>Even if that article shows how <a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> shines, I have great respect for the work that has gone into <a href="https://github.com/sirver/UltiSnips">UltiSnips</a>. It is still a reliable, reasonably fast plugin given the constraint it operates in (in particular, Vim compatibility requires a fair amount of Vimscript). I’ve written this article shortly after trying LuaSnip and I’m still very much evaluating it.</p>
  </details>



<h2 id="introduction">Introduction</h2>
<p><a href="https://en.wikipedia.org/wiki/Snippet_(programming)">Snippets</a> are a convenient feature of some text editors to insert and adapt reusable pieces of code. For instance, snippets for <code>for</code> loops are common, to get the tedious bits of the syntax out of the way.</p>
<p>To get this feature in Vim back in the days, I started using <a href="https://github.com/sirver/UltiSnips">UltiSnips</a>. There are <a href="https://github.com/honza/vim-snippets">default snippets sets</a> for it, and it’s easy to write custom snippets. These custom snippet can call Bash or Python scripts if you need more dynamic replacements. UltiSnips has been very powerful and has served me quite well over the past decade or so, and I have kept it when I migrated to NeoVim a few years ago.</p>
<h2 id="startup-time">Startup time</h2>
<p>Every once in a while though, I run the excellent <a href="https://github.com/rhysd/vim-startuptime">vim-startuptime</a> command to assess the impact of various configurations and plugins on the startup time of Neovim. With UltiSnips and the corresponding completion plugins in my configuration, the first few lines are:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>Extra options: []
</span></span><span style="display:flex;"><span>Measured: 10 times
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Total Average: 104.218300 msec
</span></span><span style="display:flex;"><span>Total Max:     109.719000 msec
</span></span><span style="display:flex;"><span>Total Min:     99.533000 msec
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  AVERAGE       MAX       MIN
</span></span><span style="display:flex;"><span>------------------------------
</span></span><span style="display:flex; background-color:#3d4148"><span>64.218600 70.419000 61.066000: ~/.config/nvim/init.lua
</span></span><span style="display:flex;"><span> 6.255900  7.238000  5.764000: loading packages
</span></span><span style="display:flex;"><span> 3.939000  5.338000  3.533000: ~/.local/share/nvim/site/pack/paqs/start/onedark.nvim/colors/onedark.lua
</span></span><span style="display:flex;"><span> 3.651100  4.003000  3.381000: loading rtp plugins
</span></span><span style="display:flex;"><span> 2.761600  3.957000  2.474000: expanding arguments
</span></span><span style="display:flex;"><span> 2.683900  3.035000  2.566000: reading ShaDa
</span></span><span style="display:flex;"><span> 2.418700  3.610000  2.131000: sourcing vimrc file(s)
</span></span><span style="display:flex;"><span> 2.389600  2.751000  2.228000: /usr/share/nvim/runtime/filetype.lua
</span></span><span style="display:flex;"><span> 1.954900  2.293000  1.702000: loading after plugins
</span></span><span style="display:flex;"><span> 1.842500  2.015000  1.763000: BufEnter autocommands
</span></span><span style="display:flex; background-color:#3d4148"><span> 1.583350  3.532000  0.018000: ~/.local/share/nvim/site/pack/paqs/start/ultisnips/plugin/UltiSnips.vim
</span></span><span style="display:flex;"><span> 1.027700  1.170000  0.831000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter/plugin/nvim-treesitter.lua
</span></span><span style="display:flex; background-color:#3d4148"><span> 0.968200  1.071000  0.860000: ~/.local/share/nvim/site/pack/paqs/start/cmp-nvim-ultisnips/after/plugin/cmp_nvim_ultisnips.lua
</span></span><span style="display:flex;"><span> 0.859000  1.014000  0.702000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter-textobjects/plugin/nvim-treesitter-textobjects.vim
</span></span><span style="display:flex;"><span> 0.590700  0.674000  0.524000: ~/.local/share/nvim/site/pack/paqs/start/indent-blankline.nvim/plugin/indent_blankline.vim
</span></span><span style="display:flex;"><span> 0.557200  0.817000  0.493000: init highlight
</span></span><span style="display:flex;"><span> 0.553200  0.898000  0.322000: opening buffers</span></span></code></pre></div>
<p>The <code>init.lua</code> line covers a lot of different plugins and mappings set up in that file. Besides the <a href="https://github.com/navarasu/onedark.nvim">onedark.nvim</a> colorscheme<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, the next biggest contributor is <code>~/…/ultisnips/plugin/UltiSnips.vim</code>. The script to integrate UltiSnips with <a href="https://github.com/hrsh7th/nvim-cmp">cmp</a>, <code>~/…/cmp-nvim-ultisnips/after/plugin/cmp_nvim_ultisnips.lua</code>, is not far behind. In total, we spend nearly 4 ms of the startup time for UltiSnips-related files, on top of the setup done in <code>init.lua</code>. That feels suboptimal, so I’ve been looking for a possible snippet plugin alternative.</p>
<h2 id="installing-luasnip">Installing LuaSnip</h2>
<p><a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> aims to be a faster<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> snippet engine, with support of treesitter in the snippets. It can also understand the LSP snippet “format”. Finally, it’s a pure Lua plugin, without any Python requirement, contrary to UltiSnips. That makes installation on various system much easier.</p>
<p>Its drawbacks for me are that it does not support the same snippet definition as UltiSnips and even in its own SnipMate-like syntax, backticks to execute code are not supported. As a result, I’ll have to migrate my (admittedly very small) snippet collection. Conversely, if I want to do more advanced things, I’ll have to learn the relatively complex “VS Studio Code” snippets in JSON or even the pure Lua snippets.</p>
<p>That’s said, I believe I can get the LuaSnip benefits without writing Lua snippets or JSON ones, at least to start. So let’s try and do that, using only the SnipMate-like syntax!</p>
<h2 id="overview-of-the-configuration-changes">Overview of the Configuration Changes</h2>
<p>I’ve made the following changes.</p>
<ol>
<li>Remove UltiSnips and its associated plugins, namely for completion with <a href="https://github.com/hrsh7th/nvim-cmp">cmp</a> and the telescope integration.</li>
<li>Remove the UltiSnips mappings.</li>
<li>Add LuaSnip (following the instructions in the <a href="https://github.com/L3MON4D3/LuaSnip">Readme</a>), <a href="https://github.com/saadparwaiz1/cmp_luasnip">its cmp integration</a>, a <a href="https://github.com/rafamadriz/friendly-snippets">set of snippets</a> and a <a href="https://github.com/benfowler/telescope-luasnip.nvim">telescope integration</a> along with some mappings (requires nvim 0.7+<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>):
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">vim.keymap</span>.<span style="color:#e06c75">set</span>({ <span style="color:#98c379">&#34;i&#34;</span>, <span style="color:#98c379">&#34;s&#34;</span> }, <span style="color:#98c379">&#34;&lt;C-i&gt;&#34;</span>, <span style="color:#c678dd">function</span>() <span style="color:#e06c75">require</span><span style="color:#98c379">&#39;luasnip&#39;</span>.<span style="color:#e06c75">jump</span>(<span style="color:#d19a66">1</span>) <span style="color:#c678dd">end</span>, { <span style="color:#e06c75">desc</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;LuaSnip forward jump&#34;</span> })
</span></span><span style="display:flex;"><span><span style="color:#e06c75">vim.keymap</span>.<span style="color:#e06c75">se</span>({ <span style="color:#98c379">&#34;i&#34;</span>, <span style="color:#98c379">&#34;s&#34;</span> }, <span style="color:#98c379">&#34;&lt;M-i&gt;&#34;</span>, <span style="color:#c678dd">function</span>() <span style="color:#e06c75">require</span><span style="color:#98c379">&#39;luasnip&#39;</span>.<span style="color:#e06c75">jump</span>(<span style="color:#56b6c2">-</span><span style="color:#d19a66">1</span>) <span style="color:#c678dd">end</span>, { <span style="color:#e06c75">desc</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;LuaSnip backward jump&#34;</span> })
</span></span></code></pre></div></li>
<li><a href="#migrate-my-snippet-collection">Migrate my existing snippets</a>, see below.</li>
<li>Add code to load the snippet set and my own snippet collection:
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;luasnip.loaders.from_vscode&#34;</span>).<span style="color:#e06c75">lazy_load</span>()
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;luasnip.loaders.from_snipmate&#34;</span>).<span style="color:#e06c75">lazy_load</span>({ <span style="color:#e06c75">paths</span> <span style="color:#56b6c2">=</span> {<span style="color:#98c379">&#34;./snippets&#34;</span>} })
</span></span></code></pre></div></li>
</ol>
<h3 id="after">After</h3>
<p>Let’s run vim-startuptime again. Here are the new top contributors:
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>Extra options: []
</span></span><span style="display:flex;"><span>Measured: 10 times
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Total Average: 98.251400 msec
</span></span><span style="display:flex;"><span>Total Max:     100.159000 msec
</span></span><span style="display:flex;"><span>Total Min:     96.519000 msec
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  AVERAGE       MAX       MIN
</span></span><span style="display:flex;"><span>------------------------------
</span></span><span style="display:flex; background-color:#3d4148"><span>60.469100 62.341000 59.619000: ~/.config/nvim/init.lua
</span></span><span style="display:flex;"><span> 5.991100  6.219000  5.865000: loading packages
</span></span><span style="display:flex;"><span> 4.088400  4.162000  3.942000: loading after plugins
</span></span><span style="display:flex;"><span> 3.635700  3.752000  3.415000: loading rtp plugins
</span></span><span style="display:flex;"><span> 3.561800  3.936000  3.220000: ~/.local/share/nvim/site/pack/paqs/start/onedark.nvim/colors/onedark.lua
</span></span><span style="display:flex;"><span> 2.550500  2.628000  2.492000: reading ShaDa
</span></span><span style="display:flex;"><span> 2.454200  2.525000  2.401000: expanding arguments
</span></span><span style="display:flex;"><span> 2.363000  2.420000  2.270000: /usr/share/nvim/runtime/filetype.lua
</span></span><span style="display:flex;"><span> 2.271100  2.337000  2.146000: sourcing vimrc file(s)
</span></span><span style="display:flex;"><span> 1.701000  1.767000  1.635000: BufEnter autocommands
</span></span><span style="display:flex;"><span> 1.676700  2.049000  1.366000: opening buffers
</span></span><span style="display:flex;"><span> 1.034100  1.280000  0.782000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter/plugin/nvim-treesitter.lua
</span></span><span style="display:flex;"><span> 0.901000  1.021000  0.799000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter-textobjects/plugin/nvim-treesitter-textobjects.vim
</span></span><span style="display:flex;"><span> 0.590700  0.674000  0.524000: ~/.local/share/nvim/site/pack/paqs/start/indent-blankline.nvim/plugin/indent_blankline.vim
</span></span><span style="display:flex;"><span> 0.549500  0.789000  0.514000: init highlight</span></span></code></pre></div></p>
<p>The snippet infrastructure is now absent from that list! More importantly, the overall startup time is down, and we can be confident that the new calls to load the snippets in <code>init.lua</code> are not costlier than UltiSnips settings before, because the <code>init.lua</code> line is down as well.</p>
<p>As a bonus, the snippet expansion feels slightly snappier with LuaSnip, although it might be an illusion and I don’t have hard numbers to back this claim.</p>
<h2 id="migrate-my-snippet-collection">Migrate my Snippet Collection</h2>
<p>Back to the topic of migrating existing UltiSnips snippets: LuaSnip will loudly complain when given UltiSnips snippets but it’s relatively easy to rewrite those snippets to the SnipMate-like format that LuaSnip understands.</p>
<h3 id="two-syntaxes">Two Syntaxes</h3>
<p>On the one hand, UltiSnips snippets roughly follow this syntax</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet">snippet trigger &#34;Comment&#34; option
snippet content
endsnippet
</code></pre><p>And so, my markdown snippets would look like this:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet">priority 10

snippet bjtoday &#34;“Bullet Journal”-styled date for today&#34; b
# `date +&#39;%F %A&#39;`
endsnippet
</code></pre><p>On the other hand, LuaSnip uses a simplified version of SnipMate snippets:</p>
<pre tabindex="0"><code># Comment
snippet toggle
  snippet content
</code></pre><h3 id="simple-snippets">Simple Snippets</h3>
<p>Since I heavily rely on snippet sets, I have only about 30 snippets defined in my own snippet collection. Most of them are really simple, with only a few lines and almost no interactive text. So for most of those, the process has been to simply remove the <code>priority …</code> lines and then a simple substitution command<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> did the trick.</p>
<h3 id="advanced-snippets-with-environment-variables">Advanced Snippets With Environment Variables</h3>
<p>However, there is also the case of the snippets calling external commands, like <code>date</code> in the example below:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet">snippet bjtoday &#34;“Bullet Journal”-styled date for today&#34; b
# `date +&#39;%F %A&#39;`
endsnippet
</code></pre><p>The problem is, to call arbitrary commands, one needs to define the snippets in Lua.</p>
<p>It turns out though that nearly all my snippets calling external command were actually inserting a date. And luckily LuaSnip defines “environment variables” holding just the values I need like <code>$CURRENT_MONTH</code>. So that snippet becomes:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet"># “Bullet Journal”-styled date for today
snippet bjtoday
  # ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} $CURRENT_DAY_NAME_SHORT
</code></pre><p>and we get to keep the simple SnipMate-like syntax!</p>
<p>You can find more of those environment variables in <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/util/environ.lua">the sources</a>.</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>2022-08-03: See also <a href="https://github.com/smjonas/snippet-converter.nvim">snippet-converter.nvim</a> to convert between various snippet formats.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
  </div>



<h2 id="whats-next">What’s Next</h2>
<p>I now have a slightly faster NeoVim and the snippet syntax that I use the most is simpler. However, there is more to explore!</p>
<p>So far, I’ve steered clear of writing more complicated JSON and Lua snippets. The latter would be necessary to unlock smart snippets using treesitter <a href="https://changelog.com/podcast/457#t=01:00:01.17">context</a>. I’ll look into that next, in particular to generate go error handling code.</p>
<p>This is also a very simple configuration, the impact on startup time of LuaSnip might go up slightly as we use more advanced feature, but during my tests, even using all available features, it was much lighter than UltiSnips.</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>2022-07-31: <a href="https://joly.pw/tags/luasnip/">Follow-up posts</a> dig deeper in other aspects of LuaSnip</p>
  </div>



<h2 id="resources">Resources</h2>
<ul>
<li>Many more details are covered in <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md">LuaSnip documentation</a>.</li>
<li>The <a href="https://github.com/garbas/vim-snipmate/blob/master/doc/SnipMate.txt">SnipMate help</a> contains the SnipMate snippet syntax, if you are unfamiliar with it.</li>
<li>The <a href="https://github.com/microsoft/language-server-protocol/blob/main/snippetSyntax.md">LSP snippet documentation</a> is also helpful.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>That run is actually on a fork of <a href="https://github.com/navarasu/onedark.nvim">onedark.nvim</a> that contains a massive speed up. The changes of that fork are being up streamed in <a href="https://github.com/navarasu/onedark.nvim/pull/76">this PR</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>According to that comment from the author for instance: <a href="https://github.com/L3MON4D3/LuaSnip/issues/60#issuecomment-873630664">https://github.com/L3MON4D3/LuaSnip/issues/60#issuecomment-873630664</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This code snippet uses new APIs introduced in Neovim 0.7 and the newly freed <code>&lt;C-i&gt;</code>, see <a href="https://gpanders.com/blog/whats-new-in-neovim-0-7/">https://gpanders.com/blog/whats-new-in-neovim-0-7/</a> for more details.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>For instance: <code>%s/^\(snippet \+\S\+\) &quot;\(.*\)&quot; \w\+\n\(\_.\{-}\)endsnippet/# \2^M\1^M \3^M</code>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Thanks to <a href="https://www.reddit.com/user/Miserable-Ad-7341">Miserable-Ad-7341</a> for <a href="https://www.reddit.com/r/neovim/comments/weonip/from_ultisnips_to_luasnip/iipheov/">pointing this out</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Putting READMEs on your Static Site</title><link>https://joly.pw/blog/putting-readmes-on-your-static-site/</link><pubDate>Thu, 23 Sep 2021 20:45:11 +0000</pubDate><guid>https://joly.pw/blog/putting-readmes-on-your-static-site/</guid><description>I’m happy to introduce RISS (README In Static Site), a tool that transforms a README file and insert it on your website</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>I’m happy to introduce <a href="https://cj.rs/readme-in-static-site/">RISS</a> (<em>README In Static Site</em>), a tool that transforms a README like <a href="https://github.com/cljoly/telescope-repo.nvim">this one</a> and insert it on your website, <a href="https://cj.rs/telescope-repo-nvim/">like so</a>.</p>
  </div>



<figure>
    <img loading="lazy" src="./riss-in-action.png"
         alt="A screenshot of two browser windows, showing RISS in action. On the left hand side, a project page on GitHub and on the right hand side, its website. Both share the same core content."/> <figcaption>
            <p><a href="https://cj.rs/telescope-repo-nvim/">Telescope-repo.nvim</a> GitHub page and website, automatically generated from the Readme code.</p>
        </figcaption>
</figure>

<h2 id="a-readme-is-a-projects-cover">A README is a Project’s Cover</h2>
<p>Your GitHub README is what your visitors on GitHub will encounter first. Thus, it needs to efficiently describe your project. There are a lot of <a href="https://github.com/zenika-open-source/promote-open-source-project#readme">advice online</a> on how to craft a great README.</p>
<p>So you then go on and create a great README. Wonderful! 🎉</p>
<h2 id="but-i-want-the-readme-on-my-website">But I want the README on my Website</h2>
<p>But then, you want to include your README on your static website, for instance in a <a href="https://dev.to/lornasw93/github-readme-on-portfolio-project-page-51i8">portfolio</a> or just to have a GitHub-independent homepage. You also want more control on the layout and theme than GitHub has to offer, and perhaps even <a href="https://matomo.org/">privacy</a>-<a href="https://plausible.io/">preserving</a> <a href="https://www.goatcounter.com/">analytics</a>.
This is a <a href="https://stackoverflow.com/q/16226202/4253785">common</a> <a href="https://stackoverflow.com/q/48919200/4253785">question</a> and there are options based on <a href="https://richjenks.com/github-pages-from-readme/">ajax</a> or on one <a href="https://medium.com/@bolajiayodeji/how-to-convert-github-markdown-files-to-a-simple-website-b08602d05e1c">GitHub Pages</a> website per repository.</p>
<p>However, dynamically loading the README on the client side sounds like taking a performance hit for what is actually static content. Furthermore, maintaining one full-blown GitHub Pages static website  for each small project, might not be worth it.</p>
<p>What if you could just change your README a bit so that it is ready for inclusion in your <a href="https://www.11ty.dev/">Eleventy</a> or <a href="https://gohugo.io/">Hugo</a> personal website, just like any other markdown page?</p>
<h2 id="enter-riss">Enter <a href="https://cj.rs/readme-in-static-site/">RISS</a>!</h2>
<p>I wrote <a href="https://cj.rs/readme-in-static-site/">RISS</a> (<em>README In Static Site</em>) for this very purpose.</p>
<p>With RISS, your project’s README is the source of truth. You just add simple comments for the small bits that need to differ between GitHub and your website. You then give the GitHub README to RISS as an input and the output is a file suitable for inclusion in your static site sources. As your README change and evolves, updating it is easy, you just rerun the script.</p>
<p>Let’s go over some examples!</p>
<h3 id="post-metadata">Post Metadata</h3>
<p>Hugo (and other static-site generators) need some <a href="https://gohugo.io/content-management/front-matter">metadata</a> at the start of your post, like so:</p>
<pre tabindex="0"><code>---
title: &#34;README In Static Site (RISS)&#34;
date: 2021-08-21T08:15:54
description: &#34;💎 Transform and insert your GitHub README in your static site&#34;
---
</code></pre><p>Of course, we don’t want to have this on GitHub. We thus put it in a comment, so that it remains hidden on GitHub:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#7f848e">&lt;!-- insert
</span></span></span><span style="display:flex;"><span><span style="color:#7f848e">---
</span></span></span><span style="display:flex;"><span><span style="color:#7f848e">title: &#34;README In Static Site (RISS)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#7f848e">date: 2021-08-21T08:15:54
</span></span></span><span style="display:flex;"><span><span style="color:#7f848e">description: &#34;💎 Transform and insert your GitHub readme in your static site&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#7f848e">---
</span></span></span><span style="display:flex;"><span><span style="color:#7f848e">end_insert --&gt;</span>
</span></span></code></pre></div><p>given the above comment as input, RISS outputs:</p>
<pre tabindex="0"><code>---
title: &#34;README In Static Site (RISS)&#34;
date: 2021-08-21T08:15:54
description: &#34;💎 Transform and insert your GitHub README in your static site&#34;
---
</code></pre><p>and the resulting file is usable by Hugo!</p>
<h2 id="more">More</h2>
<p>GitHub prohibits embedding arbitrary scripts for security reason. But what if you want to embed an <a href="https://asciinema.org/">asciinema</a> player on your project homepage, so that users can play the asciicast as they would a standard video?
You can have a placeholder with the link on GitHub as asciinema documentation advises and then use RISS to replace it with the full player on your website. You will find how to do that in the <a href="https://cj.rs/readme-in-static-site/">documentation</a>. Plus, here is an example README <a href="https://github.com/cljoly/telescope-repo.nvim">this one</a> doing this (and the <a href="https://cj.rs/telescope-repo-nvim/">corresponding website</a>, look for the asciicast that autoplays).</p>
<p>See also how to <a href="https://cj.rs/readme-in-static-site/">automate</a> the update of all these READMEs with, for instance, GitHub Action.</p>
<p>Thank you for reading!</p>
]]></content:encoded></item><item><title>Configuration Management</title><link>https://joly.pw/blog/my-setup/chezmoi/</link><pubDate>Fri, 03 Sep 2021 19:59:01 +0100</pubDate><guid>https://joly.pw/blog/my-setup/chezmoi/</guid><description>I use Chezmoi to manage my configuration</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>Chezmoi follows a declarative approach to configuration management. Even if you already use a configuration-management system, you should <a href="https://www.chezmoi.io/docs/comparison/">check out how it compares</a>!</p>
  </div>



<h2 id="a-custom-configuration-makes-you-feel-at-home">A Custom Configuration Makes You Feel at Home</h2>
<p>Your git aliases, shell setup, keyboard shortcuts and even your favorite fonts are tweaked for your own needs and tastes. It’s part of what makes your computer, <em>your</em> computer. All this configuration evolves over years and is slowly refined over time. It may actually be your longuest-living project.</p>
<p>Do you want to start from scratch when you switch computer? Of course not! And you wouldn’t want to break it with adventurous changes either! Versioning it in <a href="https://drewdevault.com/2019/12/30/dotfiles.html">git</a> backs up the history and eases the management of this important work. Configuration updates are also more convenient to share between machines this way.</p>
<h2 id="enter-chezmoi">Enter Chezmoi</h2>
<p>In the git-based configuration management described above, how to account for the small variations (like between your <a href="https://utf9k.net/blog/conditional-gitconfig/">work environment and personal computer</a>)? Git supports <a href="https://git.kernel.org/pub/scm/git/git.git/tree/Documentation/RelNotes/2.13.0.txt#n127">conditional inclusion of configuration files</a>, but how about the other tools? What if you want to encrypt your secrets in your config files, to publish your configuration on GitHub?</p>
<p><a href="https://www.chezmoi.io">Chezmoi</a> follows a <em>declarative approach</em>: it has a separate <em>source repository</em><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> to describe a target state, that then gets applied to your home directory. This is possible through templates, that are activated selectively for some files. The rest of this post shows how powerful templating can be.</p>
<h2 id="account-for-variations-between-machines">Account for Variations Between Machines</h2>
<p>Let’s start with a simple example. In the git configuration, you put your email, that’s then included in all your commits. Thus, you often want to have a different email in <code>.gitconfig</code> for your work and personal profiles. To tackle this issue, I put the following in my <code>.gitconfig</code> chezmoi template:</p>
<pre tabindex="0"><code>[user]
    email = {{ .email }}
</code></pre><p>and it gets rendered to this:</p>
<pre tabindex="0"><code>[user]
    email = me@my.home
</code></pre><p>on my personal computer and this:</p>
<pre tabindex="0"><code>[user]
    email = me@my.work
</code></pre><p>on my work computer. It serves a purpose similar to <a href="https://utf9k.net/blog/conditional-gitconfig/">conditionally setting your gitconfig</a>, but it is applicable to any program.</p>
<h2 id="even-more-variations-with-templates">Even More Variations with Templates</h2>
<p>Now, let’s cover a more powerful use of templates.</p>
<p>You can have multiple files generated from one using <a href="https://www.chezmoi.io/docs/templating/#using-chezmoitemplates">“global” templates</a>. For instance, if you use <a href="https://i3wm.org/">i3</a> and <a href="https://swaywm.org/">sway</a> as your windows manager, you likely have slightly different configurations, given that sway’s configuration is mostly compatible with that of i3. To keep everything in one file, while taking advantages of sway-specific features, you can (in your chezmoi repository):</p>
<ol>
<li>Put your configuration in <code>.chezmoitemplates/sway_i3</code>. You can keep any sway specific section in a templated <code>if</code> statement, like so:
<pre tabindex="0"><code>{{- if eq .f &#34;sway&#34; }}
# Sway specific stuff here, like output configuration
{{- end }}
</code></pre></li>
<li>In <code>dot_config/i3/config.tmpl</code> (and <code>dot_config/sway/config.tmpl</code>), use something like:
<pre tabindex="0"><code>{{/* Environment */}}
{{- $env := . -}}

{{/* Flavor */}}
{{- $f := &#34;i3&#34; -}} # &lt;--- Change this to &#34;sway&#34; in dot_config/sway/config.tmpl

{{- template &#34;sway_i3&#34; dict &#34;f&#34; $f &#34;env&#34; $env -}}
</code></pre>As you may have noticed, chezmoi renames some files in the source state, to encode parameters and for readability. So <code>dot_config/sway/config.tmpl</code> becomes <code>.config/sway/config</code> (<code>.tmpl</code> means the file is executed as a template).    <br>
Also, <code>$env</code> is used to keep the general template context, for instance to check the OS version in our template config file.</li>
</ol>
<h2 id="chezmoiignore">.chezmoiignore</h2>
<p>Finally, chezmoi supports an ignore file named <code>.chezmoiignore</code>. The concept is similar to the well known <code>.gitignore</code>: patterns listed in this file are ignored by chezmoi. In other words, some files are stored in your chezmoi repository, but do not reflect in your home. This can be used if you put a <code>README.md</code> in your configuration repository, as you don’t want this file in your home directory. Just write:</p>
<pre tabindex="0"><code>README.md
</code></pre><p>in <code>.chezmoiignore</code> and the <code>README.md</code> won’t be copied in your home.</p>
<p>You can do more advanced things with <code>.chezmoiignore</code> as it is a template by default. From <a href="https://www.chezmoi.io/docs/reference/#chezmoiignore">the documentation</a>:</p>
<pre tabindex="0"><code>{{- if ne .email &#34;firstname.lastname@company.com&#34; }}
# Ignore .company-directory unless configured with a company email
.company-directory # note that the pattern is not dot_company-directory
{{- end }}
</code></pre><h2 id="conclusion">Conclusion</h2>
<p>Chezmoi has lot more <a href="https://www.chezmoi.io/docs/reference/">features</a>, like <a href="https://www.chezmoi.io/docs/how-to/#encrypt-whole-files-with-gpg">encryption</a> or <a href="https://www.chezmoi.io/docs/how-to/#use-chezmoi-with-github-codespaces-visual-studio-codespaces-or-visual-studio-code-remote---containers">installation in GitHub Codespaces</a>. It is quite <a href="https://www.chezmoi.io">well documented</a>, in particular with a <a href="https://www.chezmoi.io/docs/quick-start/">quick start guide</a>.</p>
<p>I’ve switched to it almost two years ago, I don’t regret it at all!</p>
<h2 id="bonus-vim-snippet">Bonus: Vim Snippet</h2>
<p>To update the target state automatically when editing the source file in the chezmoi repo, I use this vim snippet (with the <a href="https://fishshell.com/">fish shell</a>):</p>
<pre tabindex="0"><code>autocmd BufWritePost ~/.local/share/chezmoi/* ! chezmoi apply --source-path %; or for f in (rg -l &#39;template &#34;%:t&#34;&#39;); chezmoi apply --source-path $f; end
</code></pre><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You should store this source repository in a git repository to easily revert changes and share configuration update between various machines.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>README In Static Site (RISS)</title><link>https://joly.pw/readme-in-static-site/</link><pubDate>Sat, 21 Aug 2021 08:15:54 +0100</pubDate><guid>https://joly.pw/readme-in-static-site/</guid><description>💎 Transform and insert your GitHub readme in your static site</description><content:encoded><![CDATA[<p>
<p style="display: flex; justify-content: space-between">
  <a href="https://codeberg.org/cljoly/readme-in-static-site" data-goatcounter-click="ext-codeberg-readme-in-static-site" data-goatcounter-title="cljoly/readme-in-static-site">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="none">
    <path
        d='M11.955.49A12 12 0 0 0 0 12.49a12 12 0 0 0 1.832 6.373L11.838 5.928a.187.14 0 0 1 .324 0l10.006 12.935A12 12 0 0 0 24 12.49a12 12 0 0 0-12-12 12 12 0 0 0-.045 0zm.375 6.467l4.416 16.553a12 12 0 0 0 5.137-4.213z' />
</svg></span>&nbsp;cljoly/readme-in-static-site
  </a>
  <a class="badges" href="https://github.com/cljoly/readme-in-static-site" data-goatcounter-click="ext-stargithub-readme-in-static-site" data-goatcounter-title="stars cljoly/readme-in-static-site">
    <img src="https://img.shields.io/github/stars/cljoly/readme-in-static-site?style=social" alt="Github stars for readme-in-static-site">
  </a>
</p>


<div class="badges">

</p>
<p><a href="https://github.com/cljoly/readme-in-static-site/blob/main/riss.awk">
<img alt="GitHub code size in bytes" loading="lazy" src="https://img.shields.io/github/size/cljoly/readme-in-static-site/riss.awk?color=purple"></a> 
<img alt="GitHub tag (latest SemVer)" loading="lazy" src="https://img.shields.io/github/v/tag/cljoly/readme-in-static-site?color=darkgreen&sort=semver"> <a href="https://github.com/cljoly/readme-in-static-site/actions/workflows/checks.yml">
<img alt="CI" loading="lazy" src="https://codeberg.org/cljoly/readme-in-static-site/actions/workflows/checks.yml/badge.svg"></a> <a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a></p>
<p>
<img alt="RISS in action" loading="lazy" src="/blog/putting-readmes-on-your-static-site/riss-in-action.png"></p>

</div>


<p>This <a href="#benchmark">fast</a> <a href="https://cj.rs/riss.awk">script</a> allows you to insert your GitHub README in your static site and apply transformations. For instance, you can read this <a href="https://github.com/cljoly/readme-in-static-site/blob/main/README.md">README on GitHub</a> and <a href="https://cj.rs/readme-in-static-site">on my website</a>.</p>
<h3 id="why">Why?</h3>
<p>The GitHub README of your repo needs to efficiently describe your project to GitHub’s visitor. But featuring your project on your website allows you to (among other things):</p>
<ul>
<li>have more control on the theme and layout,</li>
<li>insert scripts that GitHub would prohibit (like <a href="#replace-asciinema-image">asciinema</a>),</li>
<li>have your project’s homepage independent of your hosting platform, if you wish to change at some point.</li>
</ul>
<p>Chances are that for small projects, the page about your project is very similar to the GitHub README. Don’t duplicate efforts, describe the differences! This <a href="https://github.com/vhodges/stitcherd">has</a> <a href="https://dev.to/lornasw93/github-readme-on-portfolio-project-page-51i8">been</a> a <a href="https://richjenks.com/github-pages-from-readme/">long-awaited</a> <a href="https://medium.com/@bolajiayodeji/how-to-convert-github-markdown-files-to-a-simple-website-b08602d05e1c">feature</a>, in <a href="https://news.ycombinator.com/item?id=29305990">one</a> <a href="https://stackoverflow.com/q/48919200/4253785">form</a> or <a href="https://stackoverflow.com/a/69296054/4253785">another</a>.</p>
<p>See this <a href="https://cj.rs/blog/putting-readmes-on-your-static-site/">blog post</a> for more details.</p>
<h2 id="testimonials">Testimonials</h2>
<p><a href="https://news.ycombinator.com/item?id=29304376">
<img loading="lazy" src="https://img.shields.io/badge/dynamic/json?color=Orange&label=HackerNews&query=%24.score&url=https%3A%2F%2Fhacker-news.firebaseio.com%2Fv0%2Fitem%2F29304376.json&logo=ycombinator&color=orange"></a> <a href="https://lobste.rs/s/a4jzvv/readme_static_site_riss#c_hiil4z.json">
<img loading="lazy" src="https://img.shields.io/badge/dynamic/json?color=green&label=Lobsters&query=%24.score&url=https%3A%2F%2Flobste.rs%2Fs%2Fa4jzvv%2Freadme_static_site_riss.json&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAL0ALQAtZF7+HAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wCCBAuLt2rqugAAACMSURBVDjL1ZMxCsJQEERfRPEk9im9mGfxBpaCnaXmBraeQEidKsKz2eIb/peEIOLAwu4Ws7PMbsUAKlOwYCaWmd4aWCV1B4yXpR59R61SitwKDdBHfgPaSTsF8zOmHz5NLykAeCRqvuvCfxGcgP0YF3ZqHy7c1Yt6jfo8dCF3idvkQjcRRVQ/f6bZBC+RBoeZnlCyqwAAAABJRU5ErkJggg==&labelColor=500000"></a></p>
<p>RISS made it to the first page of <a href="https://news.ycombinator.com/item?id=29304376">HackerNews</a> and <a href="https://lobste.rs/s/a4jzvv/readme_static_site_riss">Lobsters</a> and got comments like:</p>




  <figure>
    <blockquote >
      <p>I never really understood the idea to have a separate README and index page. Glad to see i&rsquo;m not the only one :)</p>

    </blockquote>
    
  </figure>



<p><a href="https://news.ycombinator.com/item?id=29305519">southerntofu</a></p>




  <figure>
    <blockquote >
      <p>Kudos for making it reusable and not specific to a single static site generator. […]</p>

    </blockquote>
    
  </figure>



<p><a href="https://lobste.rs/s/a4jzvv/readme_static_site_riss#c_hiil4z">hannu</a></p>




  <figure>
    <blockquote >
      <p>[…] A small but good idea, I like how simple riss.awk is.</p>

    </blockquote>
    
  </figure>



<p><a href="https://news.ycombinator.com/item?id=29305070">lifthrasiir</a></p>
<h3 id="run-it-nothing-to-install">Run It (Nothing to Install)</h3>
<p>To try it with <a href="https://gohugo.io/">Hugo</a> or <a href="https://www.getzola.org/">Zola</a>, run the following in your static-site sources:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>wget https://cj.rs/riss.awk
</span></span><span style="display:flex;"><span>awk -f riss.awk /path/to/my-project/README.md &gt; content/my-project.md
</span></span></code></pre></div><p>If you don’t use Hugo or Zola, no problem! It should also work with any markdown-based static-site generator. Just put the markdown file where it makes sense for that generator.</p>
<p>To automatically update these files in your static-site sources, see <a href="#automate-with-github-actions">Automate with GitHub Actions</a> below. Since RISS is based on Awk, there is nothing to install, besides copying the script itself!</p>
<h2 id="example">Example</h2>
<h3 id="add-a-front-matter">Add a Front Matter</h3>
<p>Most static site generators require a “<a href="https://gohugo.io/getting-started/configuration/#configure-front-matter">front matter</a>” at the beginning of a markdown file to attach some metadata. But you don’t want to add this to your GitHub README! Let’s hide this on GitHub and have it in the script’s output.</p>
<p>In you .md file on GitHub, put:</p>
<pre><code>&lt;!-- insert
---
title: &quot;README In Static Site (RISS)&quot;
date: 2021-08-21T10:15:54
---
end_insert --&gt;
&lt;!-- Powered by https://cj.rs/riss --&gt;
&lt;!-- remove --&gt;

# README In Static Site (RISS)
&lt;!-- end_remove --&gt;
</code></pre>
<p>The output of the script will be:</p>
<pre><code>---
title: &quot;README In Static Site (RISS)&quot;
date: 2021-08-21T10:15:54
---
&lt;!-- Powered by https://cj.rs/riss --&gt;
</code></pre>
<p>and this piece of yaml will be hidden on GitHub!</p>
<h3 id="replace-asciinema-image">Replace Asciinema Image</h3>
<p>You can’t embed the asciinema player on GitHub for security reasons. So the <a href="https://asciinema.org/docs/embedding">asciinema documentation</a> suggests using an image there and to link it to a webpage with the player. But on your own website, you can embed this player.</p>
<p>In your .md file, put:</p>
<pre><code>&lt;!-- remove --&gt;
[![Finding the repositories with “telescope” in their name, with the README in the panel on the right](https://asciinema.org/a/427156.svg)](https://asciinema.org/a/427156)
&lt;!-- end_remove --&gt;
&lt;!-- insert
&lt;asciinema-player src=&quot;./telescope.cast&quot; poster=&quot;npt:0:04&quot;&gt;&lt;/asciinema-player&gt;
end_insert --&gt;
</code></pre>
<p>The output will contain only the asciinema player:</p>
<pre><code>&lt;asciinema-player src=&quot;./telescope.cast&quot; poster=&quot;npt:0:04&quot;&gt;&lt;/asciinema-player&gt;
</code></pre>
<p><em>Note</em>: you also need to add the JS/CSS files of the asciinema player somewhere in your theme. This <a href="https://cj.rs/gohugo-asciinema/">Hugo module</a> makes it easy.</p>
<h3 id="more">More</h3>
<p>See the <a href="https://github.com/cljoly/readme-in-static-site/blob/main/test.md">input file (typically on GitHub)</a> and the <a href="https://github.com/cljoly/readme-in-static-site/blob/main/test_output.md">output of the script</a>. You can find another real word <a href="https://github.com/cljoly/telescope-repo.nvim/blob/master/README.md">README</a> converted to a <a href="https://cj.rs/telescope-repo-nvim/">webpage</a> (this gives another example of asciinema conversion using a Hugo shortcode).</p>
<p>With some shell scripting, you could also transform all the markdown files in your repo and put them in a subdirectory of your site, so that your project’s documentation, policy, etc… lives on your site or even on a site of its own.</p>
<h3 id="your-example">Your Example!</h3>
<p>Have you used this script to transform some markdown (or other) and insert it on your website? <a href="https://github.com/cljoly/readme-in-static-site/issues/new">Open an issue</a> if you would like a link to your use case from this README!</p>
<ul>
<li><strong>telescope-repo.nvim</strong>: <a href="https://github.com/cljoly/telescope-repo.nvim/blob/master/README.md">readme</a>, <a href="https://cj.rs/telescope-repo-nvim/">website</a>; features an Asciinema clip.</li>
<li><strong>neovide</strong>: <a href="https://github.com/neovide/neovide/blob/main/README.md">readme</a>, <a href="https://neovide.dev/">first iteration of their website</a>, <a href="https://github.com/neovide/neovide/pull/1114">PR setting this up</a>. They have now moved to mdbook and that’s great! RISS makes the first iteration of your website easy and you are free to move to more complete solutions when your project grows.</li>
<li><strong>Hugo APT Repository</strong>: <a href="https://github.com/8hobbies/hugo-apt/blob/master/README.md">readme</a>, <a href="https://hugo-apt.8hob.io/">website</a>, <a href="https://github.com/8hobbies/hugo-apt/pull/12">PR setting it up</a>.</li>
</ul>
<h2 id="transformations-reference">Transformations Reference</h2>
<p>The transformations are driven by HTML comments, so that you can have different results when comments are ignored (e.g. in your GitHub README) and after executing the script on your markdown file.</p>
<h3 id="escaping">Escaping</h3>
<p><strong>It is important that your comment starts at the beginning of the line:</strong> spaces are used for escaping, meaning that if the comment has spaces at the beginning of the line, it is ignored.</p>
<p>So this is escaped</p>
<pre tabindex="0"><code>    &lt;!-- insert
</code></pre><p>but this is not</p>
<pre><code>&lt;!-- insert
</code></pre>
<h3 id="insertion">Insertion</h3>
<p>Use these two lines for text you want to have in your output, but not in the raw .md file.</p>
<pre><code>&lt;!-- insert
end_insert --&gt;
</code></pre>
<h3 id="remove">Remove</h3>
<p>Use these two comments for text you want to have in your raw .md file, but not in the output</p>
<pre><code>&lt;!-- remove --&gt;
&lt;!-- end_remove --&gt;
</code></pre>
<h2 id="spread-the-word">Spread the Word</h2>
<p>If you find this script useful, please consider inserting the following in your readme:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#7f848e">&lt;!-- Powered by https://cj.rs/riss --&gt;</span>
</span></span></code></pre></div><p>This will help other people find the script. <em>Thanks for spreading the word!</em></p>
<p>If you feel especially charitable, you could put this badge somewhere:</p>
<p><a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a></p>
<p>with for instance this code:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>[<span style="color:#e06c75">![</span>](<span style="color:#e06c75">https://img.shields.io/badge/powered%20by-riss-lightgrey</span>)](https://cj.rs/riss)
</span></span></code></pre></div><h2 id="breaking-api-changes">Breaking API Changes</h2>
<p>We follow <a href="https://semver.org/">semver</a> and any change that change would cause real world READMEs to be converted differently requires a new major version. In particular, the following is a breaking change:</p>
<ul>
<li>adding new keywords (like <code>remove</code> or <code>insert</code>), as they may be used in the README prior to their introduction in RISS,</li>
<li>changing a keywords syntax.</li>
</ul>
<h2 id="benchmark">Benchmark</h2>
<p><strong>Processes 17600 lines in 10 ms</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ <span style="color:#c678dd">for</span> i in <span style="color:#56b6c2">{</span>1..100<span style="color:#56b6c2">}</span>; <span style="color:#c678dd">do</span> shuf README.md &gt;&gt;bench.md; <span style="color:#c678dd">done</span> <span style="color:#7f848e"># Create a big md file</span>
</span></span><span style="display:flex;"><span>$ wc -l README.md
</span></span><span style="display:flex;"><span><span style="color:#d19a66">176</span> README.md
</span></span><span style="display:flex;"><span>$ wc -l bench.md
</span></span><span style="display:flex;"><span><span style="color:#d19a66">17600</span> bench.md
</span></span><span style="display:flex;"><span>$ hyperfine <span style="color:#98c379">&#39;awk -f riss.awk README.md&#39;</span> <span style="color:#98c379">&#39;awk -f riss.awk bench.md&#39;</span>
</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th style="text-align: left">Command</th>
          <th style="text-align: right">Mean [ms]</th>
          <th style="text-align: right">Min [ms]</th>
          <th style="text-align: right">Max [ms]</th>
          <th style="text-align: right">Relative</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><code>awk -f riss.awk README.md</code></td>
          <td style="text-align: right">2.8 ± 0.2</td>
          <td style="text-align: right">2.4</td>
          <td style="text-align: right">3.7</td>
          <td style="text-align: right">1.00</td>
      </tr>
      <tr>
          <td style="text-align: left"><code>awk -f riss.awk bench.md</code></td>
          <td style="text-align: right">9.7 ± 0.4</td>
          <td style="text-align: right">8.9</td>
          <td style="text-align: right">10.7</td>
          <td style="text-align: right">3.46 ± 0.30</td>
      </tr>
  </tbody>
</table>
<h2 id="automate-with-github-actions">Automate with GitHub Actions</h2>
<p>You can automatically update the markdown file in the sources of your site with GitHub Actions. Add this workflow to, for instance, <code>.github/workflows/readme.yml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#e06c75">name</span>: Update README files
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">on</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">schedule</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#e06c75">cron</span>: <span style="color:#98c379">&#39;30 */2 * * *&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">push</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">branches</span>:
</span></span><span style="display:flex;"><span>    - master
</span></span><span style="display:flex;"><span>  <span style="color:#7f848e"># To run this workflow manually from GitHub GUI</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">workflow_dispatch</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">build</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">runs-on</span>: ubuntu-latest
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">steps</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#e06c75">name</span>: Check out the repo
</span></span><span style="display:flex;"><span>      <span style="color:#e06c75">uses</span>: actions/checkout@v2
</span></span><span style="display:flex;"><span>    - <span style="color:#e06c75">name</span>: Get the latest READMEs
</span></span><span style="display:flex;"><span>      <span style="color:#e06c75">run</span>: make readme-update
</span></span><span style="display:flex;"><span>    - <span style="color:#e06c75">name</span>: Commit and push if there are changes
</span></span><span style="display:flex;"><span>      <span style="color:#e06c75">run</span>: |-<span style="color:#98c379">
</span></span></span><span style="display:flex;"><span><span style="color:#98c379">        git diff
</span></span></span><span style="display:flex;"><span><span style="color:#98c379">        git config --global user.email &#34;bot@example.com&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#98c379">        git config --global user.name &#34;bot&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#98c379">        git diff --quiet || (git add -u &amp;&amp; git commit -m &#34;Update READMEs&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#98c379">        git push</span>
</span></span></code></pre></div><p>and then your <code>Makefile</code> may contain something like:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-make" data-lang="make"><span style="display:flex;"><span><span style="color:#61afef;font-weight:bold">readme-update</span><span style="color:#56b6c2">:</span>
</span></span><span style="display:flex;"><span>	curl https://raw.githubusercontent.com/cljoly/readme-in-static-site/main/README.md | awk -f riss.awk &gt;content/readme-in-static-site.md
</span></span></code></pre></div><p>Alternatively, you might configure your repositories to trigger a website rebuild when committing on your README, for instance using <a href="https://mainawycliffe.dev/blog/github-actions-trigger-via-webhooks/">GitHub actions webhooks</a>.</p>
<h2 id="contributions-are-welcome">Contributions are Welcome!</h2>
<p>Feel free to <a href="https://github.com/cljoly/readme-in-static-site/issues/new">open an issue</a> to discuss something or to send a PR.</p>
<p>See also the <a href="#spread-the-word">Spread the Word</a> section if you would like to make more folks aware of this script.</p>
<p>
<img alt="GitHub" loading="lazy" src="https://img.shields.io/github/license/cljoly/readme-in-static-site"></p>
]]></content:encoded></item><item><title>How I Got Started with NeoVim’s Lua Configuration</title><link>https://joly.pw/blog/my-setup/nvim-0-5/</link><pubDate>Sun, 18 Jul 2021 22:57:01 +0100</pubDate><guid>https://joly.pw/blog/my-setup/nvim-0-5/</guid><description>&lt;p&gt;Four months ago I was still using &lt;a href="https://spacevim.org/"&gt;SpaceVim&lt;/a&gt; when I stumbled upon a &lt;a href="https://oroques.dev/notes/neovim-init/"&gt;blog post&lt;/a&gt; on how to configure NeoVim with Lua. I then started to create my own configuration. In this post I’ll share the learnings acquired in the process. I hope you will find this useful to create your own configuration!&lt;/p&gt;
&lt;h2 id="why-create-your-own-vim-config-from-scratch"&gt;Why create your own Vim config from scratch?&lt;/h2&gt;
&lt;p&gt;Vim has been my daily driver for about ten years. Almost from the beginning, I used Vim distributions for ease of configuration. &lt;a href="https://vim.spf13.com"&gt;Spf13 vim&lt;/a&gt; first and then &lt;a href="https://spacevim.org/"&gt;SpaceVim&lt;/a&gt;. With a distribution, one gets a lot of bells and whistle without spending too much time configuring things. So why spend hours setting up NeoVim from scratch?&lt;/p&gt;</description><content:encoded><![CDATA[<p>Four months ago I was still using <a href="https://spacevim.org/">SpaceVim</a> when I stumbled upon a <a href="https://oroques.dev/notes/neovim-init/">blog post</a> on how to configure NeoVim with Lua. I then started to create my own configuration. In this post I’ll share the learnings acquired in the process. I hope you will find this useful to create your own configuration!</p>
<h2 id="why-create-your-own-vim-config-from-scratch">Why create your own Vim config from scratch?</h2>
<p>Vim has been my daily driver for about ten years. Almost from the beginning, I used Vim distributions for ease of configuration. <a href="https://vim.spf13.com">Spf13 vim</a> first and then <a href="https://spacevim.org/">SpaceVim</a>. With a distribution, one gets a lot of bells and whistle without spending too much time configuring things. So why spend hours setting up NeoVim from scratch?</p>
<p>SpaceVim does provide a lot of options and plugins so that everyone can find the functionalities they need and discover some new useful tricks. This ends up creating long key mappings like <code>&lt;space&gt;ff</code> and leads to long startup time. For instance on my machine with an SSD drive, it needs between 1 and 2 seconds to start. This is a common problem with distributions.</p>
<p>The long startup time issue can be mitigated by starting Vim less often or by choosing a lighter Vim distribution. One could even reuse only the core and some parts of a distribution, leaving out the rest. Regarding the long key mappings issue, shorter ones could be redefined. However, this has two major drawbacks:</p>
<ul>
<li>your customized configuration lies on the moving foundations of the underlying distributions,</li>
<li>debugging is made more difficult because of the incorporation of so much code you haven’t written and don’t know well.</li>
</ul>
<p>To solve all these problems, I found it easier and less time-consuming to have my own configuration. Additional benefits include a better recall of what exists and what functionalities are implemented<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The good news with NeoVim 0.5 is that you can now use Lua and not an arcane language like VimScript<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. The result is also quite fast: with my configuration NeoVim starts in 100 to 110 <em>milliseconds</em>.</p>
<p>Starting your own configuration does not mean you can’t draw inspiration from other configurations! The next sections of this post reference guides, snippets and plugins I’ve found useful, so that you can evaluate them for inclusion into your own file.</p>
<h2 id="enjoy-the-ride">Enjoy the Ride!</h2>
<p>To tailor my experience to my liking, I tried my changes in a new instance of NeoVim after every addition of a plugin or of a set of mappings. Mappings in particular sometime looks good on the screen, but fingers don’t like it that much 😀.</p>
<p>In addition, NeoVim configuration can be notoriously complicated to debug. To quote the documentation:</p>




  <figure>
    <blockquote cite="https://neovim.io/doc/user/starting.html#bisect">
      <p>To find the cause of a problem in your config, you must &ldquo;bisect&rdquo; it.</p>

    </blockquote>
    
      <figcaption class="blockquote-caption">
        
          <cite style="text-align: right"><a href="https://neovim.io/doc/user/starting.html#bisect">https://neovim.io/doc/user/starting.html#bisect</a></cite>
          <br/>
        
        
      </figcaption>
    
  </figure>



<p>For this the previous point about regular testing helps, but I would advise for versioning your configuration, for instance with <a href="/blog/my-setup/chezmoi/">git and chezmoi</a>. With regular commits, you will be able to rely on the git bisect feature when things go wrong.</p>
<h2 id="guides">Guides</h2>
<p>Detailed guidance exists online to get started with your configuration. I got started with <a href="https://oroques.dev/notes/neovim-init/">a mostly one file Lua configuration</a>. This guide contains general explainations on where to put your Lua files, the reasoning behind the changes… If you are not familiar with NeoVim Lua configuration tricks, I strongly recommend reading the above link. See you in a bit 👋.</p>
<p>In addition, these links may prove useful:</p>
<ul>
<li><a href="https://github.com/mjlbach/defaults.nvim/blob/master/init.lua">Example <code>init.lua</code></a></li>
<li><a href="https://emilienl.medium.com/a-quick-guide-to-set-up-nvim-built-in-lsp-419bb6e91c0a">A quick guide to set up nvim built in LSP</a></li>
<li><a href="https://neovim.io/doc/lua-resources/">NeoVim’s Lua Resources</a></li>
</ul>
<h2 id="snippets-from-my-configuration">Snippets from my Configuration</h2>
<p>This section assumes basic knowledge of Lua configuration.</p>
<h3 id="mapping-hints">Mapping Hints</h3>
<figure>
    <img loading="lazy" src="../which-key-space.png"/> <figcaption>
            Pop up window showing the keys that can follow &lt;space&gt;
        </figcaption>
</figure>

<p><a href="https://github.com/folke/which-key.nvim">Which-Key</a> is a brilliant plugin to display a popup with the possible mappings as you press the keys of the shortcut. For instance, I have mapped <code>&lt;space&gt;j</code> to <code>:tabnew</code>, to open a new tab. On the figure above, I’ve just typed <code>&lt;space&gt;</code> (abbreviated <code>SPC</code>) and the keys that could be pressed then are displayed in a popup at the bottom of the screen.
What sets <code>which-key.nvim</code> apart is that you don’t have to configuration your mappings using a special function for it to work. It discovers most of your mappings right away and use them to populate the popup.</p>
<p>I’ve tweaked <code>timeoutlen</code> to get the popup more quickly. I’ve also changed labels for some keys to have shorter or more explicit names, like <code>SPC</code> for <code>&lt;space&gt;</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">wk</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;which-key&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">wk.setup</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">key_labels</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>    [<span style="color:#98c379">&#34;&lt;space&gt;&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;SPC&#34;</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#98c379">&#34;&lt;CR&gt;&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;RET&#34;</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#98c379">&#34;&lt;tab&gt;&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;TAB&#34;</span>,
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#e06c75">vim.opt</span>.<span style="color:#e06c75">timeoutlen</span> <span style="color:#56b6c2">=</span> <span style="color:#d19a66">900</span>
</span></span></code></pre></div><h4 id="mapping-choices">Mapping Choices</h4>
<p>My custom mappings are mainly around the <code>&lt;space&gt;</code> key, often with only one key following, like <code>&lt;space&gt;s</code> to write the current file. The <code>&lt;Leader&gt;</code> key is mostly left for plugins, while the <code>&lt;LocalLeader&gt;</code> is sometimes used for some buffer local mappings.</p>
<p>In general, my mappings are heavily adapted to the <a href="https://bepo.fr">BÉPO layout</a>, a Dvorak-like keyboard for French.</p>
<h3 id="clever-f">Clever-F</h3>
<p><a href="https://github.com/rhysd/clever-f.vim">rhysd/clever-f.vim</a> extends <code>f</code>, <code>t</code>… so that:</p>
<ol>
<li>When you press <code>fc</code>, all characters <code>c</code> in the current line get highlighted</li>
<li>Another press on <code>f</code> jumps to the next occurence of <code>c</code></li>
<li>A press on <code>F</code> jumps backward</li>
</ol>
<p>This makes <code>,</code> and <code>;</code> redondant and these can be remapped.</p>
<h4 id="leaders">Leaders</h4>
<p>The universal leader is by default mapped on <code>\</code>. However, it is frequently remapped to <code>,</code>, which is easier to reach. Since clever-f freed two mappings, I reuse them like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">g.mapleader</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;,&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">g.maplocalleader</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;;&#34;</span>
</span></span></code></pre></div><h3 id="buffer-jump">Buffer Jump</h3>
<p>With the awesome <a href="https://github.com/phaazon/hop.nvim">hop.nvim</a>, one can get hints to jump to various parts of a buffer (see the figure below if that does not make sense just yet). It is similiar to EasyMotions and supports jumps by line, word, characters sequences and pattern. However, in my opinion this plugin is not replacement for <code>w</code> and the like, because for short movements I find <code>w</code> or <code>f</code> shorter to type and because the need to read the screen for caracters may be quite slow compared to <code>3w</code>. It is also not a replacement for <code>/</code> because hop.nvim highlights whats visible on the current buffer. Thus it fills the void left for medium range move.</p>
<figure>
    <img loading="lazy" src="../hop-nvim-word.png"
         alt="I invoqued HopWord from line 105 and if I press dr, I’ll jump to the last word of line 116"/> <figcaption>
            Word jump example with hop.nvim<p>I invoqued <code>HopWord</code> from line 105 and if I press <code>dr</code>, I’ll jump to the last word of line 116</p>
        </figcaption>
</figure>

<p>Here is my configuration for this plugin:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;T&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopLineStart&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;T&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopLineStart&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;S&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopWord&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;S&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopWord&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;è&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopChar2&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;è&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopChar2&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;È&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopPattern&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;È&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopPattern&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#39;hop&#39;</span>).<span style="color:#e06c75">setup</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">keys</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#39;auietsrncbpovdljyxqghf&#39;</span>, <span style="color:#7f848e">-- Hint keys</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It is heavily optimized for the the <a href="https://bepo.fr">BÉPO layout</a>, whose <a href="https://en.wikipedia.org/wiki/Touch_typing#Home_row">home row</a> contains the <code>auie</code> keys on the left hand and the <code>tsrn</code> keys on the right hand. Hence the choice to put most mappings on <code>T</code> and <code>S</code>, easily reachable with the right hand and to start the hint keys with <code>auie</code> on the left hand.</p>
<h2 id="plugins">Plugins</h2>
<p>These plugin are often mostly written in Lua, although vim script plugins can be more mature. The Lua-plugin ecosystem is moving really quickly, you may find that other plugins are more suitable in a couple months.</p>
<h3 id="plugins-i-use">Plugins I use</h3>
<h4 id="telescope">Telescope</h4>
<p>I use <a href="https://github.com/nvim-telescope/telescope.nvim">telescope.nvim</a> as a fuzzy finder. It can be slow at times compared to <a href="https://github.com/lotabout/skim.vim">skim</a> or <a href="https://github.com/junegunn/fzf.vim">fzf</a>. However, it is relatively easy to script in Lua, making it easy to tightly integrate with other functions.</p>
<h4 id="completion-nvim">Completion-nvim</h4>
<p>I use <a href="https://github.com/nvim-lua/completion-nvim">nvim-lua/completion-nvim</a> in particular for its chains of completion (several completion sources are chained and sources lower in the chain are used when upper sources don’t have any results). However, <a href="https://github.com/hrsh7th/nvim-compe">nvim-compe</a> may be a better choice if you start fresh.</p>
<h4 id="complete-list">Complete List</h4>
<p>This is a list of plugins I use, roughly ordered by frequency of use.</p>

<details>
  <summary>
    Click to show the list of plugins
  </summary>


<ul>
<li>
<p><a href="https://github.com/savq/paq-nvim">paq-nvim</a> a light package manager written in lua. It supports lazy loading, although it’s easier to do with <a href="https://github.com/wbthomason/packer.nvim">packer.nvim</a></p>
</li>
<li>
<p><a href="https://github.com/folke/which-key.nvim">folke/which-key.nvim</a> a great plugin to dispaly a popup with possible mappings. It also offer a register preview, a bit like <a href="https://github.com/tversteeg/registers.nvim">tversteeg/registers.nvim</a></p>
<ul>
<li><a href="https://github.com/kosayoda/nvim-lightbulb">kosayoda/nvim-lightbulb</a> displays a lightbulb when a code action is available</li>
</ul>
</li>
<li>
<p>Completions &amp; snippets:</p>
<ul>
<li><a href="https://github.com/neovim/nvim-lspconfig/">nvim-lspconfig</a> and <a href="https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md">the guide for LSP Configurations</a> to use with the native LSP client in neovim.</li>
<li><a href="https://github.com/nvim-lua/completion-nvim">nvim-lua/completion-nvim</a> with these additional completion sources.
<ul>
<li><a href="https://github.com/nvim-treesitter/completion-treesitter">nvim-treesitter/completion-treesitter</a></li>
<li><a href="https://github.com/steelsojka/completion-buffers">steelsojka/completion-buffers</a></li>
</ul>
</li>
<li><a href="https://github.com/SirVer/ultisnips">SirVer/ultisnips</a> a well known snippets engine, see also <a href="https://github.com/honza/vim-snippets">honza/vim-snippets</a></li>
</ul>
</li>
<li>
<p>Mostly used with Git:</p>
<ul>
<li><a href="https://github.com/tpope/vim-fugitive">tpope/vim-fugitive</a> the well known git plugin
<ul>
<li><a href="https://github.com/tpope/vim-rhubarb">tpope/vim-rhubarb</a> :GBrowse for github</li>
</ul>
</li>
<li><a href="https://github.com/airblade/vim-gitgutter">airblade/vim-gitgutter</a> to see and manage git changes</li>
<li><a href="https://github.com/rhysd/conflict-marker.vim">rhysd/conflict-marker.vim</a> adds shortcuts to solve merge conflicts</li>
<li><a href="https://github.com/rhysd/committia.vim">rhysd/committia.vim</a> improves editing of commit messages with diff and status</li>
<li><a href="https://github.com/airblade/vim-rooter">airblade/vim-rooter</a> change to a project’s root directory</li>
</ul>
</li>
<li>
<p>Navigation:</p>
<ul>
<li><a href="https://github.com/phaazon/hop.nvim">phaazon/hop.nvim</a> a wonderful plugin that highlights text with short keystrokes to jump around</li>
<li><a href="https://github.com/rhysd/clever-f.vim">rhysd/clever-f.vim</a> &ndash; Better movements, frees “,” and “;” for <a href="#leaders">leaders</a></li>
<li><a href="https://github.com/arp242/jumpy.vim">arp242/jumpy.vim</a> allows to make big and precise jumps for instance from function to function</li>
</ul>
</li>
<li>
<p>Deal with symbols like <code>(</code> <code>[</code> or <code>&lt;</code></p>
<ul>
<li><a href="https://github.com/tpope/vim-surround">tpope/vim-surround</a> the well known plugin to add, change or delete symbols around text.</li>
<li><a href="https://github.com/jiangmiao/auto-pairs">jiangmiao/auto-pairs</a> closes pairs of symbols</li>
</ul>
</li>
<li>
<p><a href="https://github.com/ojroques/nvim-hardline">ojroques/nvim-hardline</a> a light and fast status line</p>
</li>
<li>
<p><a href="https://github.com/nvim-treesitter/nvim-treesitter">nvim-treesitter/nvim-treesitter</a>, one of the new big features of NeoVim 0.5. This is still in an early phase but it can already improve highlighting, refactoring and completions, sometimes with companion plugins like:</p>
<ul>
<li><a href="https://github.com/nvim-treesitter/nvim-treesitter-textobjects">nvim-treesitter/nvim-treesitter-textobjects</a></li>
<li><a href="https://github.com/nvim-treesitter/nvim-treesitter-refactor">nvim-treesitter/nvim-treesitter-refactor</a></li>
<li><a href="https://github.com/nvim-treesitter/playground">nvim-treesitter/playground</a></li>
</ul>
</li>
<li>
<p>Other convenience plugins</p>
<ul>
<li><a href="https://github.com/glepnir/indent-guides.nvim">glepnir/indent-guides.nvim</a> displays indentation guides.</li>
<li><a href="https://github.com/ojroques/nvim-bufdel">ojroques/nvim-bufdel</a> improve the deletion of buffers</li>
<li><a href="https://github.com/ojroques/vim-oscyank">ojroques/vim-oscyank</a> a that works across terminals, even over SSH</li>
<li><a href="https://github.com/winston0410/commented.nvim">winston0410/commented.nvim</a> (un)comment lines. It is the only lua comment plugin I’ve found that supports using <code>3gcc</code> to comment 3 lines,.</li>
<li><a href="https://github.com/dstein64/vim-startuptime">dstein64/vim-startuptime</a> a better interface to vim startup time profiles</li>
<li><a href="https://github.com/norcalli/nvim-colorizer.lua">norcalli/nvim-colorizer.lua</a> a plugin to highlight color codes with the right colors</li>
</ul>
</li>
<li>
<p><a href="https://github.com/nvim-telescope/telescope.nvim">nvim-telescope/telescope.nvim</a> a fuzzy finder with preview. I also use these extensions:</p>
<ul>
<li><a href="https://github.com/nvim-telescope/telescope-symbols.nvim">nvim-telescope/telescope-symbols.nvim</a></li>
<li><a href="https://github.com/fhill2/telescope-ultisnips.nvim">fhill2/telescope-ultisnips.nvim</a></li>
<li><a href="https://github.com/nvim-telescope/telescope-github.nvim">nvim-telescope/telescope-github.nvim</a></li>
</ul>
</li>
<li>
<p><a href="https://github.com/cj-rs/vim-bepo">cj-rs/vim-bepo</a> a customized fork of a plugin to adapt mappings to the <a href="https://bepo.fr/wiki/Accueil">bepo layout</a></p>
</li>
<li>
<p>If you work with more exotic syntaxes:</p>
<ul>
<li><a href="https://github.com/sheerun/vim-polyglot">sheerun/vim-polyglot</a></li>
<li><a href="https://github.com/cstrahan/vim-capnp">cstrahan/vim-capnp</a>

</details>

</li>
</ul>
</li>
</ul>
<h3 id="plugins-i-load-lazily">Plugins I load lazily</h3>
<p>I lazy load less often used plugins, so that they are not loaded at startup by default.</p>
<ul>
<li><a href="https://github.com/romgrk/nvim-treesitter-context">romgrk/nvim-treesitter-context</a> is a bit slow and buggy, but useful sometimes to get the context piled up at the top</li>
<li><a href="https://github.com/mattn/emmet-vim">mattn/emmet-vim</a> CSS abbreviations to generate HTML and other</li>
<li><a href="https://github.com/jbyuki/instant.nvim">jbyuki/instant.nvim</a> for remote pair programming</li>
</ul>
<h3 id="plugins-on-my-radar-for-the-future">Plugins on my Radar for the Future</h3>
<p>Plugins I might use in the future to replace existing pieces or add features:</p>
<ul>
<li><a href="https://github.com/puremourning/vimspector">vimspector</a> a debugger based on the DAP protocol (like LSP, but for debugguers)</li>
<li><a href="https://github.com/glepnir/lspsaga.nvim">lspsaga.nvim</a> a UI for the native LSP client</li>
<li><a href="https://github.com/hrsh7th/nvim-compe">nvim-compe</a> a completion engine with more advanced support for LSP. I quite like the chains in completion-nvim, but maybe nvim-compe could be a better fit at some point</li>
<li><a href="https://github.com/lambdalisue/gina.vim">gina.vim</a> to replace fugitive. It offers faster startup time and asynchronous git operations</li>
</ul>
<h2 id="other-interesting-configurations">Other Interesting Configurations</h2>
<ul>
<li><a href="https://github.com/tjdevries/config_manager/tree/master/xdg_config/nvim">tjdevries</a></li>
<li><a href="https://github.com/ojroques/dotfiles/tree/master/nvim">ojroques</a></li>
</ul>
<h2 id="thanks">Thanks</h2>
<p>Thanks to <a href="https://moverest.xyz">moverest</a> and <a href="https://jguer.space/">J. Guereiro</a> for reviewing drafts of this post.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>That was my experience at least and that of <a href="https://news.ycombinator.com/item?id=27715513">others</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Some parts of the NeoVim Lua API are not mature and you end up running some VimScript. But this is mostly true for things like mappings, which are closer to settings toggle than scripts. Even if you really want to define some mappings by scripting, you can mix Lua and VimScript like in <a href="#buffer-jump">buffer jump</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>